Check in php implementation. (#2052)

This pull request includes two implementation: C extension and PHP
package. Both implementations support encode/decode of singular,
repeated and map fields.
diff --git a/.gitignore b/.gitignore
index 052dd14..980f740 100644
--- a/.gitignore
+++ b/.gitignore
@@ -118,3 +118,18 @@
 conformance/nonexistent_tests.txt
 conformance/protoc_middleman
 conformance/succeeding_tests.txt
+
+# php test output
+composer.lock
+php/ext/google/protobuf/.libs/
+php/ext/google/protobuf/Makefile.fragments
+php/ext/google/protobuf/Makefile.global
+php/ext/google/protobuf/Makefile.objects
+php/ext/google/protobuf/acinclude.m4
+php/ext/google/protobuf/build/
+php/ext/google/protobuf/config.h
+php/ext/google/protobuf/config.nice
+php/ext/google/protobuf/configure.in
+php/ext/google/protobuf/mkinstalldirs
+php/ext/google/protobuf/run-tests.php
+vendor/
diff --git a/README.md b/README.md
index 67be3e9..32c1992 100644
--- a/README.md
+++ b/README.md
@@ -65,7 +65,7 @@
 | JavaScript                           | [js](js)                                              |
 | Ruby                                 | [ruby](ruby)                                          |
 | Go                                   | [golang/protobuf](https://github.com/golang/protobuf) |
-| PHP                                  | TBD                                                   |
+| PHP                                  | [php](php)                                            |
 
 
 Usage
diff --git a/cmake/extract_includes.bat.in b/cmake/extract_includes.bat.in
index 9edafca..7481f0c 100644
--- a/cmake/extract_includes.bat.in
+++ b/cmake/extract_includes.bat.in
@@ -32,6 +32,7 @@
 copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\objectivec\objectivec_generator.h include\google\protobuf\compiler\objectivec\objectivec_generator.h
 copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\objectivec\objectivec_helpers.h include\google\protobuf\compiler\objectivec\objectivec_helpers.h
 copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\parser.h include\google\protobuf\compiler\parser.h
+copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\php\php_generator.h include\google\protobuf\compiler\php\php_generator.h
 copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\plugin.h include\google\protobuf\compiler\plugin.h
 copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\plugin.pb.h include\google\protobuf\compiler\plugin.pb.h
 copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\python\python_generator.h include\google\protobuf\compiler\python\python_generator.h
diff --git a/cmake/libprotoc.cmake b/cmake/libprotoc.cmake
index 8df8986..71c3494 100644
--- a/cmake/libprotoc.cmake
+++ b/cmake/libprotoc.cmake
@@ -84,6 +84,7 @@
   ${protobuf_source_dir}/src/google/protobuf/compiler/objectivec/objectivec_message_field.cc
   ${protobuf_source_dir}/src/google/protobuf/compiler/objectivec/objectivec_oneof.cc
   ${protobuf_source_dir}/src/google/protobuf/compiler/objectivec/objectivec_primitive_field.cc
+  ${protobuf_source_dir}/src/google/protobuf/compiler/php/php_generator.cc
   ${protobuf_source_dir}/src/google/protobuf/compiler/plugin.cc
   ${protobuf_source_dir}/src/google/protobuf/compiler/plugin.pb.cc
   ${protobuf_source_dir}/src/google/protobuf/compiler/python/python_generator.cc
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..8b73a2b
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,24 @@
+{
+  "name": "google/protobuf",
+  "type": "library",
+  "description": "proto library for PHP",
+  "keywords": ["proto"],
+  "homepage": "https://developers.google.com/protocol-buffers/",
+  "license": "BSD-3-Clause",
+  "require": {
+    "php": ">=5.5.0"
+  },
+  "require-dev": {
+    "phpunit/phpunit": ">=4.8.0"
+  },
+  "autoload": {
+    "psr-4": {
+      "Google\\": "php/src/Google"
+    },
+    "files": [
+      "php/src/Google/Protobuf/descriptor.php",
+      "php/src/Google/Protobuf/descriptor_internal.pb.php",
+      "php/src/Google/Protobuf/Internal/Type.php"
+    ]
+  }
+}
diff --git a/php/README.md b/php/README.md
new file mode 100644
index 0000000..2f5380f
--- /dev/null
+++ b/php/README.md
@@ -0,0 +1,99 @@
+This directory contains the Protocol Buffers runtime implementation via both a
+pure PHP package and a native c extension. The pure PHP package is intended to
+provide usability to wider range of PHP platforms, while the c extension is
+intended to provide higher performance. Both implementations provide the same
+runtime APIs and share the same generated code. Users don’t need to re-generate
+code for the same proto definition when they want to switch the implementation
+later.
+
+Both implementations make use of generated PHP code that defines message and
+enum types in PHP. We strongly recommend using protoc's PHP generation support
+with .proto files. The build process in this directory only installs the
+extension/package; you need to install protoc as well to have PHP code
+generation functionality.
+
+## Requirements
+
+To use PHP runtime library requires:
+
+- PHP 5.5 or above.
+
+## Installation
+
+### C Extension
+
+#### Prerequirements
+
+To install the c extension, the following tools are needed:
+* autoconf
+* automake
+* libtool
+* make
+* gcc
+* pear
+* pecl
+
+On Ubuntu, you can install them with:
+```
+sudo apt-get install php-pear php5-dev autoconf automake libtool make gcc
+```
+On other platforms, please use the corresponding package managing tool to
+install them before proceeding.
+
+#### Installation from Source (Building extension)
+
+To build the c extension, run the following command:
+```
+cd ext/google/protobuf
+pear package
+sudo pecl install protobuf-{VERSION}.tgz
+```
+
+#### Installation from PECL
+
+When we release a version of Protocol Buffers, we will upload the extension to
+[PECL](https://pecl.php.net/). To use this pre-packaged extension, simply
+install it as you would any other extension:
+
+```
+sudo pecl install protobuf-{VERSION}
+```
+
+### PHP Package
+
+#### Installation from composer
+
+Simply add "google/protobuf" to the 'require' section of composer.json in your
+project.
+
+### Protoc
+
+Once the extension or package is installed, if you wish to generate PHP code
+from a `.proto` file, you will also want to install the Protocol Buffers
+compiler (protoc), as described in this repository's main `README` file.  The
+version of `protoc` included in the latest release supports the `--php_out`
+option to generate PHP code:
+```
+protoc --php_out=out_dir test.proto
+```
+
+## Usage
+
+For general guide:
+  https://developers.google.com/protocol-buffers/phptutorial/
+For generated code:
+  https://developers.google.com/protocol-buffers/docs/reference/php-generated
+
+Known Issues
+------------
+
+* Missing native support for well known types.
+* Missing support for proto2.
+* No API provided for clear/copy messages.
+* No API provided for encoding/decoding with stream.
+* Map fields may not be garbage-collected if there is cycle reference.
+* No debug information for messages in c extension.
+* HHVM not tested.
+* PHP 7.0 not tested.
+* C extension not tested on windows.
+* Message name cannot be Empty.
diff --git a/php/ext/google/protobuf/array.c b/php/ext/google/protobuf/array.c
new file mode 100644
index 0000000..fde4f72
--- /dev/null
+++ b/php/ext/google/protobuf/array.c
@@ -0,0 +1,388 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <ext/spl/spl_iterators.h>
+#include <Zend/zend_API.h>
+#include <Zend/zend_interfaces.h>
+
+#include "protobuf.h"
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetGet, 0, 0, 1)
+  ZEND_ARG_INFO(0, index)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetSet, 0, 0, 2)
+  ZEND_ARG_INFO(0, index)
+  ZEND_ARG_INFO(0, newval)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_void, 0)
+ZEND_END_ARG_INFO()
+
+static zend_function_entry repeated_field_methods[] = {
+  PHP_ME(RepeatedField, __construct,  NULL,              ZEND_ACC_PUBLIC)
+  PHP_ME(RepeatedField, append,       NULL,              ZEND_ACC_PUBLIC)
+  PHP_ME(RepeatedField, offsetExists, arginfo_offsetGet, ZEND_ACC_PUBLIC)
+  PHP_ME(RepeatedField, offsetGet,    arginfo_offsetGet, ZEND_ACC_PUBLIC)
+  PHP_ME(RepeatedField, offsetSet,    arginfo_offsetSet, ZEND_ACC_PUBLIC)
+  PHP_ME(RepeatedField, offsetUnset,  arginfo_offsetGet, ZEND_ACC_PUBLIC)
+  PHP_ME(RepeatedField, count,        arginfo_void,      ZEND_ACC_PUBLIC)
+  ZEND_FE_END
+};
+
+// Forward declare static functions.
+
+static zend_object_value repeated_field_create(zend_class_entry *ce TSRMLS_DC);
+static void repeated_field_free(void *object TSRMLS_DC);
+static int repeated_field_array_init(zval *array, upb_fieldtype_t type,
+                                     uint size ZEND_FILE_LINE_DC);
+static void repeated_field_free_element(void *object);
+static void repeated_field_write_dimension(zval *object, zval *offset,
+                                           zval *value TSRMLS_DC);
+static int repeated_field_has_dimension(zval *object, zval *offset TSRMLS_DC);
+static HashTable *repeated_field_get_gc(zval *object, zval ***table,
+                                        int *n TSRMLS_DC);
+
+// -----------------------------------------------------------------------------
+// RepeatedField creation/desctruction
+// -----------------------------------------------------------------------------
+
+zend_class_entry* repeated_field_type;
+zend_object_handlers* repeated_field_handlers;
+
+void repeated_field_init(TSRMLS_D) {
+  zend_class_entry class_type;
+  const char* class_name = "Google\\Protobuf\\Internal\\RepeatedField";
+  INIT_CLASS_ENTRY_EX(class_type, class_name, strlen(class_name),
+                      repeated_field_methods);
+
+  repeated_field_type = zend_register_internal_class(&class_type TSRMLS_CC);
+  repeated_field_type->create_object = repeated_field_create;
+
+  zend_class_implements(repeated_field_type TSRMLS_CC, 2, spl_ce_ArrayAccess,
+                        spl_ce_Countable);
+
+  repeated_field_handlers = PEMALLOC(zend_object_handlers);
+  memcpy(repeated_field_handlers, zend_get_std_object_handlers(),
+         sizeof(zend_object_handlers));
+  repeated_field_handlers->get_gc = repeated_field_get_gc;
+}
+
+static zend_object_value repeated_field_create(zend_class_entry *ce TSRMLS_DC) {
+  zend_object_value retval = {0};
+  RepeatedField *intern;
+
+  intern = emalloc(sizeof(RepeatedField));
+  memset(intern, 0, sizeof(RepeatedField));
+
+  zend_object_std_init(&intern->std, ce TSRMLS_CC);
+  object_properties_init(&intern->std, ce);
+
+  intern->array = NULL;
+  intern->type = 0;
+  intern->msg_ce = NULL;
+
+  retval.handle = zend_objects_store_put(
+      intern, (zend_objects_store_dtor_t)zend_objects_destroy_object,
+      (zend_objects_free_object_storage_t)repeated_field_free, NULL TSRMLS_CC);
+  retval.handlers = repeated_field_handlers;
+
+  return retval;
+}
+
+static void repeated_field_free(void *object TSRMLS_DC) {
+  RepeatedField *intern = object;
+  zend_object_std_dtor(&intern->std TSRMLS_CC);
+  zval_ptr_dtor(&intern->array);
+  efree(object);
+}
+
+static int repeated_field_array_init(zval *array, upb_fieldtype_t type,
+                                     uint size ZEND_FILE_LINE_DC) {
+  ALLOC_HASHTABLE(Z_ARRVAL_P(array));
+
+  switch (type) {
+    case UPB_TYPE_STRING:
+    case UPB_TYPE_BYTES:
+    case UPB_TYPE_MESSAGE:
+      zend_hash_init(Z_ARRVAL_P(array), size, NULL, ZVAL_PTR_DTOR, 0);
+      break;
+    default:
+      zend_hash_init(Z_ARRVAL_P(array), size, NULL, repeated_field_free_element,
+                     0);
+  }
+  Z_TYPE_P(array) = IS_ARRAY;
+  return SUCCESS;
+}
+
+static void repeated_field_free_element(void *object) {
+}
+
+// -----------------------------------------------------------------------------
+// RepeatedField Handlers
+// -----------------------------------------------------------------------------
+
+static void repeated_field_write_dimension(zval *object, zval *offset,
+                                           zval *value TSRMLS_DC) {
+  uint64_t index;
+
+  RepeatedField *intern = zend_object_store_get_object(object TSRMLS_CC);
+  HashTable *ht = HASH_OF(intern->array);
+  int size = native_slot_size(intern->type);
+
+  unsigned char memory[NATIVE_SLOT_MAX_SIZE];
+  memset(memory, 0, NATIVE_SLOT_MAX_SIZE);
+
+  if (!native_slot_set(intern->type, intern->msg_ce, memory, value)) {
+    return;
+  }
+
+  if (!offset || Z_TYPE_P(offset) == IS_NULL) {
+    index = zend_hash_num_elements(HASH_OF(intern->array));
+  } else {
+    if (protobuf_convert_to_uint64(offset, &index)) {
+      if (!zend_hash_index_exists(ht, index)) {
+        zend_error(E_USER_ERROR, "Element at %d doesn't exist.\n", index);
+        return;
+      }
+    } else {
+      return;
+    }
+  }
+
+  zend_hash_index_update(ht, index, memory, size, NULL);
+}
+
+static HashTable *repeated_field_get_gc(zval *object, zval ***table,
+                                        int *n TSRMLS_DC) {
+  *table = NULL;
+  *n = 0;
+  RepeatedField *intern = zend_object_store_get_object(object TSRMLS_CC);
+  return HASH_OF(intern->array);
+}
+
+// -----------------------------------------------------------------------------
+// C RepeatedField Utilities
+// -----------------------------------------------------------------------------
+
+void *repeated_field_index_native(RepeatedField *intern, int index) {
+  HashTable *ht = HASH_OF(intern->array);
+  void *value;
+
+  if (zend_hash_index_find(ht, index, (void **)&value) == FAILURE) {
+    zend_error(E_USER_ERROR, "Element at %d doesn't exist.\n", index);
+    return NULL;
+  }
+
+  return value;
+}
+
+void repeated_field_push_native(RepeatedField *intern, void *value TSRMLS_DC) {
+  HashTable *ht = HASH_OF(intern->array);
+  int size = native_slot_size(intern->type);
+  zend_hash_next_index_insert(ht, (void **)value, size, NULL);
+}
+
+void repeated_field_create_with_type(zend_class_entry *ce,
+                                     const upb_fielddef *field,
+                                     zval **repeated_field TSRMLS_DC) {
+  MAKE_STD_ZVAL(*repeated_field);
+  Z_TYPE_PP(repeated_field) = IS_OBJECT;
+  Z_OBJVAL_PP(repeated_field) =
+      repeated_field_type->create_object(repeated_field_type TSRMLS_CC);
+
+  RepeatedField *intern =
+      zend_object_store_get_object(*repeated_field TSRMLS_CC);
+  intern->type = upb_fielddef_type(field);
+  if (intern->type == UPB_TYPE_MESSAGE) {
+    upb_msgdef *msg = upb_fielddef_msgsubdef(field);
+    zval *desc_php = get_def_obj(msg);
+    Descriptor *desc = zend_object_store_get_object(desc_php TSRMLS_CC);
+    intern->msg_ce = desc->klass;
+  }
+  MAKE_STD_ZVAL(intern->array);
+  repeated_field_array_init(intern->array, intern->type, 0 ZEND_FILE_LINE_CC);
+
+  // TODO(teboring): Link class entry for message and enum
+}
+
+
+// -----------------------------------------------------------------------------
+// PHP RepeatedField Methods
+// -----------------------------------------------------------------------------
+
+/**
+ * Constructs an instance of RepeatedField.
+ * @param long Type of the stored element.
+ * @param string Message/Enum class name (message/enum fields only).
+ */
+PHP_METHOD(RepeatedField, __construct) {
+  long type;
+  zend_class_entry* klass = NULL;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|C", &type, &klass) ==
+      FAILURE) {
+    return;
+  }
+
+  RepeatedField *intern = zend_object_store_get_object(getThis() TSRMLS_CC);
+  intern->type = to_fieldtype(type);
+  intern->msg_ce = klass;
+
+  MAKE_STD_ZVAL(intern->array);
+  repeated_field_array_init(intern->array, intern->type, 0 ZEND_FILE_LINE_CC);
+
+  if (intern->type == UPB_TYPE_MESSAGE && klass == NULL) {
+    zend_error(E_USER_ERROR, "Message type must have concrete class.");
+    return;
+  }
+
+  // TODO(teboring): Consider enum.
+}
+
+/**
+ * Append element to the end of the repeated field.
+ * @param object The element to be added.
+ */
+PHP_METHOD(RepeatedField, append) {
+  zval *value;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) ==
+      FAILURE) {
+    return;
+  }
+  repeated_field_write_dimension(getThis(), NULL, value TSRMLS_CC);
+}
+
+/**
+ * Check whether the element at given index exists.
+ * @param long The index to be checked.
+ * @return bool True if the element at the given index exists.
+ */
+PHP_METHOD(RepeatedField, offsetExists) {
+  long index;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) ==
+      FAILURE) {
+    return;
+  }
+
+  RepeatedField *intern = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+  RETURN_BOOL(index >= 0 &&
+              index < zend_hash_num_elements(HASH_OF(intern->array)));
+}
+
+/**
+ * Return the element at the given index.
+ * This will also be called for: $ele = $arr[0]
+ * @param long The index of the element to be fetched.
+ * @return object The stored element at given index.
+ * @exception Invalid type for index.
+ * @exception Non-existing index.
+ */
+PHP_METHOD(RepeatedField, offsetGet) {
+  long index;
+  void *memory;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) ==
+      FAILURE) {
+    return;
+  }
+
+  RepeatedField *intern = zend_object_store_get_object(getThis() TSRMLS_CC);
+  HashTable *table = HASH_OF(intern->array);
+
+  if (zend_hash_index_find(table, index, (void **)&memory) == FAILURE) {
+    zend_error(E_USER_ERROR, "Element at %d doesn't exist.\n", index);
+    return;
+  }
+
+  native_slot_get(intern->type, memory, return_value_ptr TSRMLS_CC);
+}
+
+/**
+ * Assign the element at the given index.
+ * This will also be called for: $arr []= $ele and $arr[0] = ele
+ * @param long The index of the element to be assigned.
+ * @param object The element to be assigned.
+ * @exception Invalid type for index.
+ * @exception Non-existing index.
+ * @exception Incorrect type of the element.
+ */
+PHP_METHOD(RepeatedField, offsetSet) {
+  zval *index, *value;
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &index, &value) ==
+      FAILURE) {
+    return;
+  }
+  repeated_field_write_dimension(getThis(), index, value TSRMLS_CC);
+}
+
+/**
+ * Remove the element at the given index.
+ * This will also be called for: unset($arr)
+ * @param long The index of the element to be removed.
+ * @exception Invalid type for index.
+ * @exception The element to be removed is not at the end of the RepeatedField.
+ */
+PHP_METHOD(RepeatedField, offsetUnset) {
+  long index;
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) ==
+      FAILURE) {
+    return;
+  }
+
+  RepeatedField *intern = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+  // Only the element at the end of the array can be removed.
+  if (index == -1 ||
+      index != (zend_hash_num_elements(HASH_OF(intern->array)) - 1)) {
+    zend_error(E_USER_ERROR, "Cannot remove element at %d.\n", index);
+    return;
+  }
+
+  zend_hash_index_del(HASH_OF(intern->array), index);
+}
+
+/**
+ * Return the number of stored elements.
+ * This will also be called for: count($arr)
+ * @return long The number of stored elements.
+ */
+PHP_METHOD(RepeatedField, count) {
+  RepeatedField *intern = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+  if (zend_parse_parameters_none() == FAILURE) {
+    return;
+  }
+
+  RETURN_LONG(zend_hash_num_elements(HASH_OF(intern->array)));
+}
diff --git a/php/ext/google/protobuf/config.m4 b/php/ext/google/protobuf/config.m4
index b5946f7..ab032e4 100644
--- a/php/ext/google/protobuf/config.m4
+++ b/php/ext/google/protobuf/config.m4
@@ -1,10 +1,10 @@
-dnl lines starting with "dnl" are comments
-
 PHP_ARG_ENABLE(protobuf, whether to enable Protobuf extension, [  --enable-protobuf   Enable Protobuf extension])
 
 if test "$PHP_PROTOBUF" != "no"; then
 
-  dnl this defines the extension
-  PHP_NEW_EXTENSION(protobuf, upb.c protobuf.c def.c message.c storage.c, $ext_shared)
+  PHP_NEW_EXTENSION(
+    protobuf,
+    array.c def.c encode_decode.c map.c message.c protobuf.c storage.c type_check.c upb.c utf8.c,
+    $ext_shared)
 
 fi
diff --git a/php/ext/google/protobuf/def.c b/php/ext/google/protobuf/def.c
index fc18806..cbd0ec6 100644
--- a/php/ext/google/protobuf/def.c
+++ b/php/ext/google/protobuf/def.c
@@ -1,115 +1,213 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
 #include "protobuf.h"
 
+// Forward declare.
+static zend_object_value descriptor_create(zend_class_entry *ce TSRMLS_DC);
+static void descriptor_init_c_instance(Descriptor* intern TSRMLS_DC);
+static void descriptor_free_c(Descriptor* object TSRMLS_DC);
+static void descriptor_free(void* object TSRMLS_DC);
+
+static zend_object_value enum_descriptor_create(zend_class_entry *ce TSRMLS_DC);
+static void enum_descriptor_init_c_instance(EnumDescriptor* intern TSRMLS_DC);
+static void enum_descriptor_free_c(EnumDescriptor* object TSRMLS_DC);
+static void enum_descriptor_free(void* object TSRMLS_DC);
+
+static zend_object_value descriptor_pool_create(zend_class_entry *ce TSRMLS_DC);
+static void descriptor_pool_free_c(DescriptorPool* object TSRMLS_DC);
+static void descriptor_pool_free(void* object TSRMLS_DC);
+static void descriptor_pool_init_c_instance(DescriptorPool* pool TSRMLS_DC);
+
 // -----------------------------------------------------------------------------
 // Common Utilities
 // -----------------------------------------------------------------------------
 
-void check_upb_status(const upb_status* status, const char* msg) {
+static void check_upb_status(const upb_status* status, const char* msg) {
   if (!upb_ok(status)) {
-    zend_error("%s: %s\n", msg, upb_status_errmsg(status));
+    zend_error(E_ERROR, "%s: %s\n", msg, upb_status_errmsg(status));
   }
 }
 
+static void upb_filedef_free(void *r) {
+  upb_filedef *f = *(upb_filedef **)r;
+  size_t i;
 
-static upb_def *check_notfrozen(const upb_def *def) {
-  if (upb_def_isfrozen(def)) {
-    zend_error(E_ERROR,
-               "Attempt to modify a frozen descriptor. Once descriptors are "
-               "added to the descriptor pool, they may not be modified.");
+  for (i = 0; i < upb_filedef_depcount(f); i++) {
+    upb_filedef_unref(upb_filedef_dep(f, i), f);
   }
-  return (upb_def *)def;
+
+  upb_inttable_uninit(&f->defs);
+  upb_inttable_uninit(&f->deps);
+  upb_gfree((void *)f->name);
+  upb_gfree((void *)f->package);
+  upb_gfree(f);
 }
 
-static upb_msgdef *check_msgdef_notfrozen(const upb_msgdef *def) {
-  return upb_downcast_msgdef_mutable(check_notfrozen((const upb_def *)def));
+// Camel-case the field name and append "Entry" for generated map entry name.
+// e.g. map<KeyType, ValueType> foo_map => FooMapEntry
+static void append_map_entry_name(char *result, const char *field_name,
+                                  int pos) {
+  bool cap_next = true;
+  int i;
+
+  for (i = 0; i < strlen(field_name); ++i) {
+    if (field_name[i] == '_') {
+      cap_next = true;
+    } else if (cap_next) {
+      // Note: Do not use ctype.h due to locales.
+      if ('a' <= field_name[i] && field_name[i] <= 'z') {
+        result[pos++] = field_name[i] - 'a' + 'A';
+      } else {
+        result[pos++] = field_name[i];
+      }
+      cap_next = false;
+    } else {
+      result[pos++] = field_name[i];
+    }
+  }
+  strcat(result, "Entry");
 }
 
-static upb_fielddef *check_fielddef_notfrozen(const upb_fielddef *def) {
-  return upb_downcast_fielddef_mutable(check_notfrozen((const upb_def *)def));
-}
+#define CHECK_UPB(code, msg)             \
+  do {                                   \
+    upb_status status = UPB_STATUS_INIT; \
+    code;                                \
+    check_upb_status(&status, msg);      \
+  } while (0)
 
-#define PROTOBUF_WRAP_INTERN(wrapper, intern, intern_dtor)            \
-  Z_TYPE_P(wrapper) = IS_OBJECT;                                      \
-  Z_OBJVAL_P(wrapper)                                                 \
-      .handle = zend_objects_store_put(                               \
-      intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, \
-      intern_dtor, NULL TSRMLS_CC);                                   \
-  Z_OBJVAL_P(wrapper).handlers = zend_get_std_object_handlers();
-
-#define PROTOBUF_SETUP_ZEND_WRAPPER(class_name, class_name_lower, wrapper,    \
-                                    intern)                                   \
-  Z_TYPE_P(wrapper) = IS_OBJECT;                                              \
-  class_name *intern = ALLOC(class_name);                                     \
-  memset(intern, 0, sizeof(class_name));                                      \
-  class_name_lower##_init_c_instance(intern TSRMLS_CC);                       \
-  Z_OBJVAL_P(wrapper)                                                         \
-      .handle = zend_objects_store_put(intern, NULL, class_name_lower##_free, \
-                                       NULL TSRMLS_CC);                       \
-  Z_OBJVAL_P(wrapper).handlers = zend_get_std_object_handlers();
-
-#define PROTOBUF_CREATE_ZEND_WRAPPER(class_name, class_name_lower, wrapper, \
-                                     intern)                                \
-  MAKE_STD_ZVAL(wrapper);                                                   \
-  PROTOBUF_SETUP_ZEND_WRAPPER(class_name, class_name_lower, wrapper, intern);
-
-#define DEFINE_CLASS(name, name_lower, string_name)                          \
-  zend_class_entry *name_lower##_type;                                       \
+// Define PHP class
+#define DEFINE_PROTOBUF_INIT_CLASS(name_lower, string_name)                  \
   void name_lower##_init(TSRMLS_D) {                                         \
     zend_class_entry class_type;                                             \
     INIT_CLASS_ENTRY(class_type, string_name, name_lower##_methods);         \
     name_lower##_type = zend_register_internal_class(&class_type TSRMLS_CC); \
     name_lower##_type->create_object = name_lower##_create;                  \
-  }                                                                          \
-  name *php_to_##name_lower(zval *val TSRMLS_DC) {                           \
-    return (name *)zend_object_store_get_object(val TSRMLS_CC);              \
-  }                                                                          \
-  void name_lower##_free(void *object TSRMLS_DC) {                           \
-    name *intern = (name *)object;                                           \
-    name_lower##_free_c(intern TSRMLS_CC);                                   \
-    efree(object);                                                           \
-  }                                                                          \
-  zend_object_value name_lower##_create(zend_class_entry *ce TSRMLS_DC) {    \
-    zend_object_value return_value;                                          \
-    name *intern = (name *)emalloc(sizeof(name));                            \
-    memset(intern, 0, sizeof(name));                                         \
-    name_lower##_init_c_instance(intern TSRMLS_CC);                          \
-    return_value.handle = zend_objects_store_put(                            \
-        intern, (zend_objects_store_dtor_t)zend_objects_destroy_object,      \
-        name_lower##_free, NULL TSRMLS_CC);                                  \
-    return_value.handlers = zend_get_std_object_handlers();                  \
-    return return_value;                                                     \
   }
 
+#define DEFINE_PROTOBUF_CREATE(name, name_lower)                        \
+  static zend_object_value name_lower##_create(                         \
+      zend_class_entry* ce TSRMLS_DC) {                                 \
+    zend_object_value return_value;                                     \
+    name* intern = (name*)emalloc(sizeof(name));                        \
+    memset(intern, 0, sizeof(name));                                    \
+    name_lower##_init_c_instance(intern TSRMLS_CC);                     \
+    return_value.handle = zend_objects_store_put(                       \
+        intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, \
+        name_lower##_free, NULL TSRMLS_CC);                             \
+    return_value.handlers = zend_get_std_object_handlers();             \
+    return return_value;                                                \
+  }
+
+#define DEFINE_PROTOBUF_FREE(name, name_lower)            \
+  static void name_lower##_free(void* object TSRMLS_DC) { \
+    name* intern = (name*)object;                         \
+    name_lower##_free_c(intern TSRMLS_CC);                \
+    efree(object);                                        \
+  }
+
+#define DEFINE_CLASS(name, name_lower, string_name) \
+  zend_class_entry* name_lower##_type;              \
+  DEFINE_PROTOBUF_FREE(name, name_lower)            \
+  DEFINE_PROTOBUF_CREATE(name, name_lower)          \
+  DEFINE_PROTOBUF_INIT_CLASS(name_lower, string_name)
+
+// -----------------------------------------------------------------------------
+// GPBType
+// -----------------------------------------------------------------------------
+
+zend_class_entry* gpb_type_type;
+
+static zend_function_entry gpb_type_methods[] = {
+  ZEND_FE_END
+};
+
+void gpb_type_init(TSRMLS_D) {
+  zend_class_entry class_type;
+  INIT_CLASS_ENTRY(class_type, "Google\\Protobuf\\Internal\\GPBType",
+                   gpb_type_methods);
+  gpb_type_type = zend_register_internal_class(&class_type TSRMLS_CC);
+  zend_declare_class_constant_long(gpb_type_type, STR("DOUBLE"),  1 TSRMLS_CC);
+  zend_declare_class_constant_long(gpb_type_type, STR("FLOAT"),   2 TSRMLS_CC);
+  zend_declare_class_constant_long(gpb_type_type, STR("INT64"),   3 TSRMLS_CC);
+  zend_declare_class_constant_long(gpb_type_type, STR("UINT64"),  4 TSRMLS_CC);
+  zend_declare_class_constant_long(gpb_type_type, STR("INT32"),   5 TSRMLS_CC);
+  zend_declare_class_constant_long(gpb_type_type, STR("FIXED64"), 6 TSRMLS_CC);
+  zend_declare_class_constant_long(gpb_type_type, STR("FIXED32"), 7 TSRMLS_CC);
+  zend_declare_class_constant_long(gpb_type_type, STR("BOOL"),    8 TSRMLS_CC);
+  zend_declare_class_constant_long(gpb_type_type, STR("STRING"),  9 TSRMLS_CC);
+  zend_declare_class_constant_long(gpb_type_type, STR("GROUP"),   10 TSRMLS_CC);
+  zend_declare_class_constant_long(gpb_type_type, STR("MESSAGE"), 11 TSRMLS_CC);
+  zend_declare_class_constant_long(gpb_type_type, STR("BYTES"),   12 TSRMLS_CC);
+  zend_declare_class_constant_long(gpb_type_type, STR("UINT32"),  13 TSRMLS_CC);
+  zend_declare_class_constant_long(gpb_type_type, STR("ENUM"),    14 TSRMLS_CC);
+  zend_declare_class_constant_long(gpb_type_type, STR("SFIXED32"),
+                                   15 TSRMLS_CC);
+  zend_declare_class_constant_long(gpb_type_type, STR("SFIXED64"),
+                                   16 TSRMLS_CC);
+  zend_declare_class_constant_long(gpb_type_type, STR("SINT32"), 17 TSRMLS_CC);
+  zend_declare_class_constant_long(gpb_type_type, STR("SINT64"), 18 TSRMLS_CC);
+}
+
 // -----------------------------------------------------------------------------
 // DescriptorPool
 // -----------------------------------------------------------------------------
 
 static zend_function_entry descriptor_pool_methods[] = {
-  PHP_ME(DescriptorPool, addMessage, NULL, ZEND_ACC_PUBLIC)
-  PHP_ME(DescriptorPool, finalize, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(DescriptorPool, getGeneratedPool, NULL,
+         ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+  PHP_ME(DescriptorPool, internalAddGeneratedFile, NULL, ZEND_ACC_PUBLIC)
   ZEND_FE_END
 };
 
 DEFINE_CLASS(DescriptorPool, descriptor_pool,
-             "Google\\Protobuf\\DescriptorPool");
+             "Google\\Protobuf\\Internal\\DescriptorPool");
 
+zval* generated_pool_php;  // wrapper of generated pool
 DescriptorPool *generated_pool;  // The actual generated pool
 
-ZEND_FUNCTION(get_generated_pool) {
-  if (PROTOBUF_G(generated_pool) == NULL) {
-    MAKE_STD_ZVAL(PROTOBUF_G(generated_pool));
-    Z_TYPE_P(PROTOBUF_G(generated_pool)) = IS_OBJECT;
+static void init_generated_pool_once(TSRMLS_D) {
+  if (generated_pool_php == NULL) {
+    MAKE_STD_ZVAL(generated_pool_php);
+    Z_TYPE_P(generated_pool_php) = IS_OBJECT;
     generated_pool = ALLOC(DescriptorPool);
     descriptor_pool_init_c_instance(generated_pool TSRMLS_CC);
-    Z_OBJ_HANDLE_P(PROTOBUF_G(generated_pool)) = zend_objects_store_put(
+    Z_OBJ_HANDLE_P(generated_pool_php) = zend_objects_store_put(
         generated_pool, NULL,
-        (zend_objects_free_object_storage_t)descriptor_pool_free, NULL TSRMLS_CC);
-    Z_OBJ_HT_P(PROTOBUF_G(generated_pool)) = zend_get_std_object_handlers();
+        (zend_objects_free_object_storage_t)descriptor_pool_free,
+        NULL TSRMLS_CC);
+    Z_OBJ_HT_P(generated_pool_php) = zend_get_std_object_handlers();
   }
-  RETURN_ZVAL(PROTOBUF_G(generated_pool), 1, 0);
 }
 
-void descriptor_pool_init_c_instance(DescriptorPool* pool TSRMLS_DC) {
+static void descriptor_pool_init_c_instance(DescriptorPool *pool TSRMLS_DC) {
   zend_object_std_init(&pool->std, descriptor_pool_type TSRMLS_CC);
   pool->symtab = upb_symtab_new(&pool->symtab);
 
@@ -117,31 +215,21 @@
   zend_hash_init(pool->pending_list, 1, NULL, ZVAL_PTR_DTOR, 0);
 }
 
-void descriptor_pool_free_c(DescriptorPool *pool TSRMLS_DC) {
+static void descriptor_pool_free_c(DescriptorPool *pool TSRMLS_DC) {
   upb_symtab_unref(pool->symtab, &pool->symtab);
+
   zend_hash_destroy(pool->pending_list);
   FREE_HASHTABLE(pool->pending_list);
 }
 
-PHP_METHOD(DescriptorPool, addMessage) {
-  char *name = NULL;
-  int str_len;
-  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &str_len) ==
-      FAILURE) {
-    return;
+static void validate_enumdef(const upb_enumdef *enumdef) {
+  // Verify that an entry exists with integer value 0. (This is the default
+  // value.)
+  const char *lookup = upb_enumdef_iton(enumdef, 0);
+  if (lookup == NULL) {
+    zend_error(E_USER_ERROR,
+               "Enum definition does not contain a value for '0'.");
   }
-
-  zval* retval = NULL;
-  PROTOBUF_CREATE_ZEND_WRAPPER(MessageBuilderContext, message_builder_context,
-                               retval, context);
-
-  MAKE_STD_ZVAL(context->pool);
-  ZVAL_ZVAL(context->pool, getThis(), 1, 0);
-
-  Descriptor *desc = php_to_descriptor(context->descriptor TSRMLS_CC);
-  Descriptor_name_set(desc, name);
-
-  RETURN_ZVAL(retval, 0, 1);
 }
 
 static void validate_msgdef(const upb_msgdef* msgdef) {
@@ -157,35 +245,123 @@
   }
 }
 
-PHP_METHOD(DescriptorPool, finalize) {
-  DescriptorPool *self = php_to_descriptor_pool(getThis() TSRMLS_CC);
-  Bucket *temp;
-  int i, num;
+PHP_METHOD(DescriptorPool, getGeneratedPool) {
+  init_generated_pool_once(TSRMLS_C);
+  RETURN_ZVAL(generated_pool_php, 1, 0);
+}
 
-  num = zend_hash_num_elements(self->pending_list);
-  upb_def **defs = emalloc(sizeof(upb_def *) * num);
+static void convert_to_class_name_inplace(char *proto_name,
+                                          size_t pkg_name_len) {
+  size_t i;
+  bool first_char = false;
 
-  for (i = 0, temp = self->pending_list->pListHead; temp != NULL;
-       temp = temp->pListNext) {
-    zval *def_php = *(zval **)temp->pData;
-    Descriptor* desc = php_to_descriptor(def_php TSRMLS_CC);
-    defs[i] = (upb_def *)desc->msgdef;
-    validate_msgdef((const upb_msgdef *)defs[i++]);
+  for (i = 0; i <= pkg_name_len + 1; i++) {
+    // PHP package uses camel case.
+    if (!first_char && proto_name[i] != '.') {
+      first_char = true;
+      proto_name[i] += 'A' - 'a';
+    }
+    // php packages are divided by '\'.
+    if (proto_name[i] == '.') {
+      first_char = false;
+      proto_name[i] = '\\';
+    }
   }
 
-  CHECK_UPB(upb_symtab_add(self->symtab, (upb_def **)defs, num, NULL, &status),
-            "Unable to add defs to DescriptorPool");
+  // Submessage is concatenated with its containing messages by '_'.
+  for (i = pkg_name_len; i < strlen(proto_name); i++) {
+    if (proto_name[i] == '.') {
+      proto_name[i] = '_';
+    }
+  }
+}
 
-  for (temp = self->pending_list->pListHead; temp != NULL;
-       temp = temp->pListNext) {
-    // zval *def_php = *(zval **)temp->pData;
-    // Descriptor* desc = php_to_descriptor(def_php TSRMLS_CC);
-    build_class_from_descriptor((zval *)temp->pDataPtr TSRMLS_CC);
+PHP_METHOD(DescriptorPool, internalAddGeneratedFile) {
+  char *data = NULL;
+  int data_len;
+  upb_filedef **files;
+  size_t i;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &data, &data_len) ==
+      FAILURE) {
+    return;
   }
 
-  FREE(defs);
-  zend_hash_destroy(self->pending_list);
-  zend_hash_init(self->pending_list, 1, NULL, ZVAL_PTR_DTOR, 0);
+  DescriptorPool *pool = UNBOX(DescriptorPool, getThis());
+  CHECK_UPB(files = upb_loaddescriptor(data, data_len, &pool, &status),
+            "Parse binary descriptors to internal descriptors failed");
+
+  // This method is called only once in each file.
+  assert(files[0] != NULL);
+  assert(files[1] == NULL);
+
+  CHECK_UPB(upb_symtab_addfile(pool->symtab, files[0], &status),
+            "Unable to add file to DescriptorPool");
+
+  // For each enum/message, we need its PHP class, upb descriptor and its PHP
+  // wrapper. These information are needed later for encoding, decoding and type
+  // checking. However, sometimes we just have one of them. In order to find
+  // them quickly, here, we store the mapping for them.
+  for (i = 0; i < upb_filedef_defcount(files[0]); i++) {
+    const upb_def *def = upb_filedef_def(files[0], i);
+    switch (upb_def_type(def)) {
+#define CASE_TYPE(def_type, def_type_lower, desc_type, desc_type_lower)        \
+  case UPB_DEF_##def_type: {                                                   \
+    desc_type *desc;                                                           \
+    zval *desc_php;                                                            \
+    CREATE(desc_type, desc, desc_type_lower##_init_c_instance);                \
+    BOX(desc_type, desc_php, desc, desc_type_lower##_free);                    \
+    Z_DELREF_P(desc_php);                                                      \
+    const upb_##def_type_lower *def_type_lower =                               \
+        upb_downcast_##def_type_lower(def);                                    \
+    desc->def_type_lower = def_type_lower;                                     \
+    add_def_obj(desc->def_type_lower, desc_php);                               \
+    /* Unlike other messages, MapEntry is shared by all map fields and doesn't \
+     * have generated PHP class.*/                                             \
+    if (upb_def_type(def) == UPB_DEF_MSG && upb_msgdef_mapentry(def)) {        \
+      break;                                                                   \
+    }                                                                          \
+    /* Prepend '.' to package name to make it absolute. */                     \
+    const char *fullname = upb_##def_type_lower##_fullname(def_type_lower);    \
+    char *klass_name = ecalloc(sizeof(char), 2 + strlen(fullname));            \
+    klass_name[0] = '.';                                                       \
+    strcpy(&klass_name[1], fullname);                                          \
+    size_t pkg_name_len = strlen(upb_filedef_package(files[0]));               \
+    convert_to_class_name_inplace(klass_name, pkg_name_len);                   \
+    zend_class_entry **pce;                                                    \
+    if (zend_lookup_class(klass_name, strlen(klass_name), &pce TSRMLS_CC) ==   \
+        FAILURE) {                                                             \
+      zend_error(E_ERROR, "Generated message class %s hasn't been defined",    \
+                 klass_name);                                                  \
+      return;                                                                  \
+    } else {                                                                   \
+      desc->klass = *pce;                                                      \
+    }                                                                          \
+    add_ce_obj(desc->klass, desc_php);                                         \
+    efree(klass_name);                                                         \
+    break;                                                                     \
+  }
+
+      CASE_TYPE(MSG, msgdef, Descriptor, descriptor)
+      CASE_TYPE(ENUM, enumdef, EnumDescriptor, enum_descriptor)
+#undef CASE_TYPE
+
+      default:
+        break;
+    }
+  }
+
+  for (i = 0; i < upb_filedef_defcount(files[0]); i++) {
+    const upb_def *def = upb_filedef_def(files[0], i);
+    if (upb_def_type(def) == UPB_DEF_MSG) {
+      const upb_msgdef *msgdef = upb_downcast_msgdef(def);
+      zval *desc_php = get_def_obj(msgdef);
+      build_class_from_descriptor(desc_php TSRMLS_CC);
+    }
+  }
+
+  upb_filedef_unref(files[0], &pool);
+  upb_gfree(files);
 }
 
 // -----------------------------------------------------------------------------
@@ -196,186 +372,87 @@
   ZEND_FE_END
 };
 
-DEFINE_CLASS(Descriptor, descriptor, "Google\\Protobuf\\Descriptor");
+DEFINE_CLASS(Descriptor, descriptor, "Google\\Protobuf\\Internal\\Descriptor");
 
-void descriptor_free_c(Descriptor *self TSRMLS_DC) {
-  upb_msg_field_iter iter;
-  upb_msg_field_begin(&iter, self->msgdef);
-  while (!upb_msg_field_done(&iter)) {
-    upb_fielddef *fielddef = upb_msg_iter_field(&iter);
-    upb_fielddef_unref(fielddef, &fielddef);
-    upb_msg_field_next(&iter);
-  }
-  upb_msgdef_unref(self->msgdef, &self->msgdef);
+static void descriptor_free_c(Descriptor *self TSRMLS_DC) {
   if (self->layout) {
     free_layout(self->layout);
   }
+  if (self->fill_handlers) {
+    upb_handlers_unref(self->fill_handlers, &self->fill_handlers);
+  }
+  if (self->fill_method) {
+    upb_pbdecodermethod_unref(self->fill_method, &self->fill_method);
+  }
+  if (self->pb_serialize_handlers) {
+    upb_handlers_unref(self->pb_serialize_handlers,
+                       &self->pb_serialize_handlers);
+  }
 }
 
-static void descriptor_add_field(Descriptor *desc,
-                                 const upb_fielddef *fielddef) {
-  upb_msgdef *mut_def = check_msgdef_notfrozen(desc->msgdef);
-  upb_fielddef *mut_field_def = check_fielddef_notfrozen(fielddef);
-  CHECK_UPB(upb_msgdef_addfield(mut_def, mut_field_def, NULL, &status),
-            "Adding field to Descriptor failed");
-  // add_def_obj(fielddef, obj);
-}
-
-void descriptor_init_c_instance(Descriptor* desc TSRMLS_DC) {
+static void descriptor_init_c_instance(Descriptor *desc TSRMLS_DC) {
   zend_object_std_init(&desc->std, descriptor_type TSRMLS_CC);
-  desc->msgdef = upb_msgdef_new(&desc->msgdef);
+  desc->msgdef = NULL;
   desc->layout = NULL;
-  // MAKE_STD_ZVAL(intern->klass);
-  // ZVAL_NULL(intern->klass);
+  desc->klass = NULL;
+  desc->fill_handlers = NULL;
+  desc->fill_method = NULL;
   desc->pb_serialize_handlers = NULL;
 }
 
-void Descriptor_name_set(Descriptor *desc, const char *name) {
-  upb_msgdef *mut_def = check_msgdef_notfrozen(desc->msgdef);
-  CHECK_UPB(upb_msgdef_setfullname(mut_def, name, &status),
-            "Error setting Descriptor name");
+// -----------------------------------------------------------------------------
+// EnumDescriptor
+// -----------------------------------------------------------------------------
+
+static zend_function_entry enum_descriptor_methods[] = {
+  ZEND_FE_END
+};
+
+DEFINE_CLASS(EnumDescriptor, enum_descriptor,
+             "Google\\Protobuf\\Internal\\EnumDescriptor");
+
+static void enum_descriptor_free_c(EnumDescriptor *self TSRMLS_DC) {
+}
+
+static void enum_descriptor_init_c_instance(EnumDescriptor *self TSRMLS_DC) {
+  zend_object_std_init(&self->std, enum_descriptor_type TSRMLS_CC);
+  self->enumdef = NULL;
+  self->klass = NULL;
 }
 
 // -----------------------------------------------------------------------------
 // FieldDescriptor
 // -----------------------------------------------------------------------------
 
-static void field_descriptor_name_set(const upb_fielddef* fielddef,
-                                      const char *name) {
-  upb_fielddef *mut_def = check_fielddef_notfrozen(fielddef);
-  CHECK_UPB(upb_fielddef_setname(mut_def, name, &status),
-            "Error setting FieldDescriptor name");
-}
+upb_fieldtype_t to_fieldtype(upb_descriptortype_t type) {
+  switch (type) {
+#define CASE(descriptor_type, type)           \
+  case UPB_DESCRIPTOR_TYPE_##descriptor_type: \
+    return UPB_TYPE_##type;
 
-static void field_descriptor_label_set(const upb_fielddef* fielddef,
-                                       upb_label_t upb_label) {
-  upb_fielddef *mut_def = check_fielddef_notfrozen(fielddef);
-  upb_fielddef_setlabel(mut_def, upb_label);
-}
-
-upb_fieldtype_t string_to_descriptortype(const char *type) {
-#define CONVERT(upb, str)   \
-  if (!strcmp(type, str)) { \
-    return UPB_DESCRIPTOR_TYPE_##upb;  \
-  }
-
-  CONVERT(FLOAT, "float");
-  CONVERT(DOUBLE, "double");
-  CONVERT(BOOL, "bool");
-  CONVERT(STRING, "string");
-  CONVERT(BYTES, "bytes");
-  CONVERT(MESSAGE, "message");
-  CONVERT(GROUP, "group");
-  CONVERT(ENUM, "enum");
-  CONVERT(INT32, "int32");
-  CONVERT(INT64, "int64");
-  CONVERT(UINT32, "uint32");
-  CONVERT(UINT64, "uint64");
-  CONVERT(SINT32, "sint32");
-  CONVERT(SINT64, "sint64");
-  CONVERT(FIXED32, "fixed32");
-  CONVERT(FIXED64, "fixed64");
-  CONVERT(SFIXED32, "sfixed32");
-  CONVERT(SFIXED64, "sfixed64");
+  CASE(FLOAT,    FLOAT);
+  CASE(DOUBLE,   DOUBLE);
+  CASE(BOOL,     BOOL);
+  CASE(STRING,   STRING);
+  CASE(BYTES,    BYTES);
+  CASE(MESSAGE,  MESSAGE);
+  CASE(GROUP,    MESSAGE);
+  CASE(ENUM,     ENUM);
+  CASE(INT32,    INT32);
+  CASE(INT64,    INT64);
+  CASE(UINT32,   UINT32);
+  CASE(UINT64,   UINT64);
+  CASE(SINT32,   INT32);
+  CASE(SINT64,   INT64);
+  CASE(FIXED32,  UINT32);
+  CASE(FIXED64,  UINT64);
+  CASE(SFIXED32, INT32);
+  CASE(SFIXED64, INT64);
 
 #undef CONVERT
 
-  zend_error(E_ERROR, "Unknown field type.");
-  return 0;
-}
-
-static void field_descriptor_type_set(const upb_fielddef* fielddef,
-                                      const char *type) {
-  upb_fielddef *mut_def = check_fielddef_notfrozen(fielddef);
-  upb_fielddef_setdescriptortype(mut_def, string_to_descriptortype(type));
-}
-
-static void field_descriptor_number_set(const upb_fielddef* fielddef,
-                                        int number) {
-  upb_fielddef *mut_def = check_fielddef_notfrozen(fielddef);
-  CHECK_UPB(upb_fielddef_setnumber(mut_def, number, &status),
-            "Error setting field number");
-}
-
-// -----------------------------------------------------------------------------
-// MessageBuilderContext
-// -----------------------------------------------------------------------------
-
-static zend_function_entry message_builder_context_methods[] = {
-    PHP_ME(MessageBuilderContext, finalizeToPool, NULL, ZEND_ACC_PUBLIC)
-    PHP_ME(MessageBuilderContext, optional, NULL, ZEND_ACC_PUBLIC)
-    {NULL, NULL, NULL}
-};
-
-DEFINE_CLASS(MessageBuilderContext, message_builder_context,
-             "Google\\Protobuf\\Internal\\MessageBuilderContext");
-
-void message_builder_context_free_c(MessageBuilderContext *context TSRMLS_DC) {
-  zval_ptr_dtor(&context->descriptor);
-  zval_ptr_dtor(&context->pool);
-}
-
-void message_builder_context_init_c_instance(
-    MessageBuilderContext *context TSRMLS_DC) {
-  zend_object_std_init(&context->std, message_builder_context_type TSRMLS_CC);
-  PROTOBUF_CREATE_ZEND_WRAPPER(Descriptor, descriptor, context->descriptor,
-                               desc);
-}
-
-static void msgdef_add_field(Descriptor *desc, upb_label_t upb_label,
-                             const char *name, const char *type, int number,
-                             const char *type_class) {
-  upb_fielddef *fielddef = upb_fielddef_new(&fielddef);
-  upb_fielddef_setpacked(fielddef, false);
-
-  field_descriptor_label_set(fielddef, upb_label);
-  field_descriptor_name_set(fielddef, name);
-  field_descriptor_type_set(fielddef, type);
-  field_descriptor_number_set(fielddef, number);
-
-// //   if (type_class != Qnil) {
-// //     if (TYPE(type_class) != T_STRING) {
-// //       rb_raise(rb_eArgError, "Expected string for type class");
-// //     }
-// //     // Make it an absolute type name by prepending a dot.
-// //     type_class = rb_str_append(rb_str_new2("."), type_class);
-// //     rb_funcall(fielddef, rb_intern("submsg_name="), 1, type_class);
-// //   }
-  descriptor_add_field(desc, fielddef);
-}
-
-PHP_METHOD(MessageBuilderContext, optional) {
-  MessageBuilderContext *self = php_to_message_builder_context(getThis() TSRMLS_CC);
-  Descriptor *desc = php_to_descriptor(self->descriptor TSRMLS_CC);
-  // VALUE name, type, number, type_class;
-  const char *name, *type, *type_class;
-  int number, name_str_len, type_str_len, type_class_str_len;
-  if (ZEND_NUM_ARGS() == 3) {
-    if (zend_parse_parameters(3 TSRMLS_CC, "ssl", &name,
-                              &name_str_len, &type, &type_str_len, &number) == FAILURE) {
-      return;
-    }
-  } else {
-    if (zend_parse_parameters(4 TSRMLS_CC, "ssls", &name,
-                              &name_str_len, &type, &type_str_len, &number, &type_class,
-                              &type_class_str_len) == FAILURE) {
-      return;
-    }
   }
 
-  msgdef_add_field(desc, UPB_LABEL_OPTIONAL, name, type, number, type_class);
-
-  zval_copy_ctor(getThis());
-  RETURN_ZVAL(getThis(), 1, 0);
-}
-
-PHP_METHOD(MessageBuilderContext, finalizeToPool) {
-  MessageBuilderContext *self = php_to_message_builder_context(getThis() TSRMLS_CC);
-  DescriptorPool *pool = php_to_descriptor_pool(self->pool TSRMLS_CC);
-  Descriptor* desc = php_to_descriptor(self->descriptor TSRMLS_CC);
-
-  Z_ADDREF_P(self->descriptor);
-  zend_hash_next_index_insert(pool->pending_list, &self->descriptor,
-                              sizeof(zval *), NULL);
-  RETURN_ZVAL(self->pool, 1, 0);
+  zend_error(E_ERROR, "Unknown field type.");
+  return 0;
 }
diff --git a/php/ext/google/protobuf/encode_decode.c b/php/ext/google/protobuf/encode_decode.c
new file mode 100644
index 0000000..4b51dab
--- /dev/null
+++ b/php/ext/google/protobuf/encode_decode.c
@@ -0,0 +1,1245 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "protobuf.h"
+
+/* stringsink *****************************************************************/
+
+typedef struct {
+  upb_byteshandler handler;
+  upb_bytessink sink;
+  char *ptr;
+  size_t len, size;
+} stringsink;
+
+
+static void *stringsink_start(void *_sink, const void *hd, size_t size_hint) {
+  stringsink *sink = _sink;
+  sink->len = 0;
+  return sink;
+}
+
+static size_t stringsink_string(void *_sink, const void *hd, const char *ptr,
+                                size_t len, const upb_bufhandle *handle) {
+  stringsink *sink = _sink;
+  size_t new_size = sink->size;
+
+  UPB_UNUSED(hd);
+  UPB_UNUSED(handle);
+
+  while (sink->len + len > new_size) {
+    new_size *= 2;
+  }
+
+  if (new_size != sink->size) {
+    sink->ptr = realloc(sink->ptr, new_size);
+    sink->size = new_size;
+  }
+
+  memcpy(sink->ptr + sink->len, ptr, len);
+  sink->len += len;
+
+  return len;
+}
+
+void stringsink_init(stringsink *sink) {
+  upb_byteshandler_init(&sink->handler);
+  upb_byteshandler_setstartstr(&sink->handler, stringsink_start, NULL);
+  upb_byteshandler_setstring(&sink->handler, stringsink_string, NULL);
+
+  upb_bytessink_reset(&sink->sink, &sink->handler, sink);
+
+  sink->size = 32;
+  sink->ptr = malloc(sink->size);
+  sink->len = 0;
+}
+
+void stringsink_uninit(stringsink *sink) { free(sink->ptr); }
+
+/* stackenv *****************************************************************/
+
+// Stack-allocated context during an encode/decode operation. Contains the upb
+// environment and its stack-based allocator, an initial buffer for allocations
+// to avoid malloc() when possible, and a template for PHP exception messages
+// if any error occurs.
+#define STACK_ENV_STACKBYTES 4096
+typedef struct {
+  upb_env env;
+  const char *php_error_template;
+  char allocbuf[STACK_ENV_STACKBYTES];
+} stackenv;
+
+
+static void stackenv_init(stackenv* se, const char* errmsg);
+static void stackenv_uninit(stackenv* se);
+
+// Callback invoked by upb if any error occurs during parsing or serialization.
+static bool env_error_func(void* ud, const upb_status* status) {
+    stackenv* se = ud;
+    // Free the env -- zend_error will longjmp up the stack past the
+    // encode/decode function so it would not otherwise have been freed.
+    stackenv_uninit(se);
+
+    // TODO(teboring): have a way to verify that this is actually a parse error,
+    // instead of just throwing "parse error" unconditionally.
+    zend_error(E_ERROR, se->php_error_template, upb_status_errmsg(status));
+    // Never reached.
+    return false;
+}
+
+static void stackenv_init(stackenv* se, const char* errmsg) {
+  se->php_error_template = errmsg;
+  upb_env_init2(&se->env, se->allocbuf, sizeof(se->allocbuf), NULL);
+  upb_env_seterrorfunc(&se->env, env_error_func, se);
+}
+
+static void stackenv_uninit(stackenv* se) {
+  upb_env_uninit(&se->env);
+}
+
+// -----------------------------------------------------------------------------
+// Parsing.
+// -----------------------------------------------------------------------------
+
+#define DEREF(msg, ofs, type) *(type*)(((uint8_t *)msg) + ofs)
+
+// Creates a handlerdata that simply contains the offset for this field.
+static const void* newhandlerdata(upb_handlers* h, uint32_t ofs) {
+  size_t* hd_ofs = (size_t*)malloc(sizeof(size_t));
+  *hd_ofs = ofs;
+  upb_handlers_addcleanup(h, hd_ofs, free);
+  return hd_ofs;
+}
+
+typedef struct {
+  size_t ofs;
+  const upb_msgdef *md;
+} submsg_handlerdata_t;
+
+// Creates a handlerdata that contains offset 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));
+  hd->ofs = ofs;
+  hd->md = upb_fielddef_msgsubdef(f);
+  upb_handlers_addcleanup(h, hd, free);
+  return hd;
+}
+
+typedef struct {
+  size_t ofs;              // union data slot
+  size_t case_ofs;         // oneof_case field
+  int property_ofs;        // properties table cache
+  uint32_t oneof_case_num; // oneof-case number to place in oneof_case field
+  const upb_msgdef *md;    // msgdef, for oneof submessage handler
+} oneof_handlerdata_t;
+
+static const void *newoneofhandlerdata(upb_handlers *h,
+                                       uint32_t ofs,
+                                       uint32_t case_ofs,
+                                       int property_ofs,
+                                       const upb_fielddef *f) {
+  oneof_handlerdata_t* hd =
+      (oneof_handlerdata_t*)malloc(sizeof(oneof_handlerdata_t));
+  hd->ofs = ofs;
+  hd->case_ofs = case_ofs;
+  hd->property_ofs = property_ofs;
+  // We reuse the field tag number as a oneof union discriminant tag. Note that
+  // we don't expose these numbers to the user, so the only requirement is that
+  // we have some unique ID for each union case/possibility. The field tag
+  // numbers are already present and are easy to use so there's no reason to
+  // create a separate ID space. In addition, using the field tag number here
+  // lets us easily look up the field in the oneof accessor.
+  hd->oneof_case_num = upb_fielddef_number(f);
+  if (upb_fielddef_type(f) == UPB_TYPE_MESSAGE) {
+    hd->md = upb_fielddef_msgsubdef(f);
+  } else {
+    hd->md = NULL;
+  }
+  upb_handlers_addcleanup(h, hd, free);
+  return hd;
+}
+
+// A handler that starts a repeated field.  Gets the Repeated*Field instance for
+// 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 (void*)(*DEREF(msg, *ofs, zval**));
+}
+
+// Handlers that append primitive values to a repeated field.
+#define DEFINE_APPEND_HANDLER(type, ctype)                             \
+  static bool append##type##_handler(void* closure, const void* hd,    \
+                                     ctype val) {                      \
+    zval* array = (zval*)closure;                                      \
+    RepeatedField* intern =                                            \
+        (RepeatedField*)zend_object_store_get_object(array TSRMLS_CC); \
+    repeated_field_push_native(intern, &val);                          \
+    return true;                                                       \
+  }
+
+DEFINE_APPEND_HANDLER(bool,   bool)
+DEFINE_APPEND_HANDLER(int32,  int32_t)
+DEFINE_APPEND_HANDLER(uint32, uint32_t)
+DEFINE_APPEND_HANDLER(float,  float)
+DEFINE_APPEND_HANDLER(int64,  int64_t)
+DEFINE_APPEND_HANDLER(uint64, uint64_t)
+DEFINE_APPEND_HANDLER(double, double)
+
+// Appends a string to a repeated field.
+static void* appendstr_handler(void *closure,
+                               const void *hd,
+                               size_t size_hint) {
+  zval* array = (zval*)closure;
+  RepeatedField* intern =
+      (RepeatedField*)zend_object_store_get_object(array TSRMLS_CC);
+
+  zval* str;
+  MAKE_STD_ZVAL(str);
+  ZVAL_STRING(str, "", 1);
+
+  repeated_field_push_native(intern, &str TSRMLS_CC);
+  return (void*)str;
+}
+
+// Appends a 'bytes' string to a repeated field.
+static void* appendbytes_handler(void *closure,
+                                 const void *hd,
+                                 size_t size_hint) {
+  zval* array = (zval*)closure;
+  RepeatedField* intern =
+      (RepeatedField*)zend_object_store_get_object(array TSRMLS_CC);
+
+  zval* str;
+  MAKE_STD_ZVAL(str);
+  ZVAL_STRING(str, "", 1);
+
+  repeated_field_push_native(intern, &str TSRMLS_CC);
+  return (void*)str;
+}
+
+static void *empty_php_string(zval** value_ptr) {
+  SEPARATE_ZVAL_IF_NOT_REF(value_ptr);
+  zval* str = *value_ptr;
+  zval_dtor(str);
+  ZVAL_STRINGL(str, "", 0, 1);
+  return (void*)str;
+}
+
+// Sets a non-repeated string field in a message.
+static void* str_handler(void *closure,
+                         const void *hd,
+                         size_t size_hint) {
+  MessageHeader* msg = closure;
+  const size_t *ofs = hd;
+  return empty_php_string(DEREF(msg, *ofs, zval**));
+}
+
+// Sets a non-repeated 'bytes' field in a message.
+static void* bytes_handler(void *closure,
+                           const void *hd,
+                           size_t size_hint) {
+  MessageHeader* msg = closure;
+  const size_t *ofs = hd;
+  return empty_php_string(DEREF(msg, *ofs, zval**));
+}
+
+static size_t stringdata_handler(void* closure, const void* hd,
+                                 const char* str, size_t len,
+                                 const upb_bufhandle* handle) {
+  zval* php_str = (zval*)closure;
+
+  char* old_str = Z_STRVAL_P(php_str);
+  size_t old_len = Z_STRLEN_P(php_str);
+  assert(old_str != NULL);
+
+  char* new_str = emalloc(old_len + len + 1);
+
+  memcpy(new_str, old_str, old_len);
+  memcpy(new_str + old_len, str, len);
+  new_str[old_len + len] = 0;
+  FREE(old_str);
+
+  Z_STRVAL_P(php_str) = new_str;
+  Z_STRLEN_P(php_str) = old_len + len;
+
+  return len;
+}
+
+// Appends a submessage to a repeated field.
+static void *appendsubmsg_handler(void *closure, const void *hd) {
+  zval* array = (zval*)closure;
+  RepeatedField* intern =
+      (RepeatedField*)zend_object_store_get_object(array TSRMLS_CC);
+
+  const submsg_handlerdata_t *submsgdata = hd;
+  zval* subdesc_php = get_def_obj((void*)submsgdata->md);
+  Descriptor* subdesc = zend_object_store_get_object(subdesc_php TSRMLS_CC);
+  zend_class_entry* subklass = subdesc->klass;
+  MessageHeader* submsg;
+
+  zval* val = NULL;
+  MAKE_STD_ZVAL(val);
+  Z_TYPE_P(val) = IS_OBJECT;
+  Z_OBJVAL_P(val) = subklass->create_object(subklass TSRMLS_CC);
+
+  repeated_field_push_native(intern, &val TSRMLS_CC);
+
+  submsg = zend_object_store_get_object(val TSRMLS_CC);
+  return submsg;
+}
+
+// Sets a non-repeated submessage field in a message.
+static void *submsg_handler(void *closure, const void *hd) {
+  MessageHeader* msg = closure;
+  const submsg_handlerdata_t* submsgdata = hd;
+  zval* subdesc_php = get_def_obj((void*)submsgdata->md);
+  Descriptor* subdesc = zend_object_store_get_object(subdesc_php TSRMLS_CC);
+  zend_class_entry* subklass = subdesc->klass;
+  zval* submsg_php;
+  MessageHeader* submsg;
+
+  if (Z_TYPE_P(*DEREF(msg, submsgdata->ofs, zval**)) == IS_NULL) {
+    zval* val = NULL;
+    MAKE_STD_ZVAL(val);
+    Z_TYPE_P(val) = IS_OBJECT;
+    Z_OBJVAL_P(val) = subklass->create_object(subklass TSRMLS_CC);
+
+    zval_ptr_dtor(DEREF(msg, submsgdata->ofs, zval**));
+    *DEREF(msg, submsgdata->ofs, zval**) = val;
+  }
+
+  submsg_php = *DEREF(msg, submsgdata->ofs, zval**);
+
+  submsg = zend_object_store_get_object(submsg_php TSRMLS_CC);
+  return submsg;
+}
+
+// Handler data for startmap/endmap handlers.
+typedef struct {
+  size_t ofs;
+  upb_fieldtype_t key_field_type;
+  upb_fieldtype_t value_field_type;
+
+  // We know that we can hold this reference because the handlerdata has the
+  // same lifetime as the upb_handlers struct, and the upb_handlers struct holds
+  // a reference to the upb_msgdef, which in turn has references to its subdefs.
+  const upb_def* value_field_subdef;
+} map_handlerdata_t;
+
+// Temporary frame for map parsing: at the beginning of a map entry message, a
+// submsg handler allocates a frame to hold (i) a reference to the Map object
+// into which this message will be inserted and (ii) storage slots to
+// temporarily hold the key and value for this map entry until the end of the
+// submessage. When the submessage ends, another handler is called to insert the
+// value into the map.
+typedef struct {
+  zval* map;
+  char key_storage[NATIVE_SLOT_MAX_SIZE];
+  char value_storage[NATIVE_SLOT_MAX_SIZE];
+} map_parse_frame_t;
+
+static void map_slot_init(void* memory, upb_fieldtype_t type) {
+  switch (type) {
+    case UPB_TYPE_STRING:
+    case UPB_TYPE_BYTES: {
+      // Store zval** in memory in order to be consistent with the layout of
+      // singular fields.
+      zval** holder = ALLOC(zval*);
+      zval* tmp;
+      MAKE_STD_ZVAL(tmp);
+      ZVAL_STRINGL(tmp, "", 0, 1);
+      *holder = tmp;
+      *(zval***)memory = holder;
+      break;
+    }
+    case UPB_TYPE_MESSAGE: {
+      zval** holder = ALLOC(zval*);
+      zval* tmp;
+      MAKE_STD_ZVAL(tmp);
+      ZVAL_NULL(tmp);
+      *holder = tmp;
+      *(zval***)memory = holder;
+      break;
+    }
+    default:
+      native_slot_init(type, memory, NULL);
+  }
+}
+
+static void map_slot_uninit(void* memory, upb_fieldtype_t type) {
+  switch (type) {
+    case UPB_TYPE_MESSAGE:
+    case UPB_TYPE_STRING:
+    case UPB_TYPE_BYTES: {
+      zval** holder = *(zval***)memory;
+      zval_ptr_dtor(holder);
+      FREE(holder);
+      break;
+    }
+    default:
+      break;
+  }
+}
+
+static void map_slot_key(upb_fieldtype_t type, const void* from, char** keyval,
+                         size_t* length) {
+  if (type == UPB_TYPE_STRING) {
+    zval* key_php = **(zval***)from;
+    *keyval = Z_STRVAL_P(key_php);
+    *length = Z_STRLEN_P(key_php);
+  } else {
+    *keyval = from;
+    *length = native_slot_size(type);
+  }
+}
+
+static void map_slot_value(upb_fieldtype_t type, const void* from, upb_value* v) {
+  size_t len;
+  void* to = upb_value_memory(v);
+#ifndef NDEBUG
+  v->ctype = UPB_CTYPE_UINT64;
+#endif
+
+  memset(to, 0, native_slot_size(type));
+
+  switch (type) {
+    case UPB_TYPE_STRING:
+    case UPB_TYPE_BYTES:
+    case UPB_TYPE_MESSAGE: {
+      *(zval**)to = **(zval***)from;
+      Z_ADDREF_PP((zval**)to);
+      break;
+    }
+    default:
+      len = native_slot_size(type);
+      memcpy(to, from, len);
+  }
+}
+
+// Handler to begin a map entry: allocates a temporary frame. This is the
+// 'startsubmsg' handler on the msgdef that contains the map field.
+static void *startmapentry_handler(void *closure, const void *hd) {
+  MessageHeader* msg = closure;
+  const map_handlerdata_t* mapdata = hd;
+  zval* map = *DEREF(msg, mapdata->ofs, zval**);
+
+  map_parse_frame_t* frame = ALLOC(map_parse_frame_t);
+  frame->map = map;
+
+  map_slot_init(&frame->key_storage, mapdata->key_field_type);
+  map_slot_init(&frame->value_storage, mapdata->value_field_type);
+
+  return frame;
+}
+
+// Handler to end a map entry: inserts the value defined during the message into
+// the map. This is the 'endmsg' handler on the map entry msgdef.
+static bool endmap_handler(void *closure, const void *hd, upb_status* s) {
+  map_parse_frame_t* frame = closure;
+  const map_handlerdata_t* mapdata = hd;
+
+  Map *map = (Map *)zend_object_store_get_object(frame->map TSRMLS_CC);
+
+  const char* keyval = NULL;
+  upb_value v;
+  size_t length;
+
+  map_slot_key(map->key_type, &frame->key_storage, &keyval, &length);
+  map_slot_value(map->value_type, &frame->value_storage, &v);
+
+  map_index_set(map, keyval, length, v);
+
+  map_slot_uninit(&frame->key_storage, mapdata->key_field_type);
+  map_slot_uninit(&frame->value_storage, mapdata->value_field_type);
+  FREE(frame);
+
+  return true;
+}
+
+// Allocates a new map_handlerdata_t given the map entry message definition. If
+// the offset of the field within the parent message is also given, that is
+// added to the handler data as well. Note that this is called *twice* per map
+// field: once in the parent message handler setup when setting the startsubmsg
+// handler and once in the map entry message handler setup when setting the
+// 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_msgdef* mapentry_def,
+    Descriptor* desc) {
+  const upb_fielddef* key_field;
+  const upb_fielddef* value_field;
+  // TODO(teboring): Use emalloc and efree.
+  map_handlerdata_t* hd =
+      (map_handlerdata_t*)malloc(sizeof(map_handlerdata_t));
+
+  hd->ofs = ofs;
+  key_field = upb_msgdef_itof(mapentry_def, MAP_KEY_FIELD);
+  assert(key_field != NULL);
+  hd->key_field_type = upb_fielddef_type(key_field);
+  value_field = upb_msgdef_itof(mapentry_def, MAP_VALUE_FIELD);
+  assert(value_field != NULL);
+  hd->value_field_type = upb_fielddef_type(value_field);
+  hd->value_field_subdef = upb_fielddef_subdef(value_field);
+
+  return hd;
+}
+
+// Handlers that set primitive values in oneofs.
+#define DEFINE_ONEOF_HANDLER(type, ctype)                           \
+  static bool oneof##type##_handler(void *closure, const void *hd,  \
+                                     ctype val) {                   \
+    const oneof_handlerdata_t *oneofdata = hd;                      \
+    DEREF(closure, oneofdata->case_ofs, uint32_t) =                 \
+        oneofdata->oneof_case_num;                                  \
+    DEREF(closure, oneofdata->ofs, ctype) = val;                    \
+    return true;                                                    \
+  }
+
+DEFINE_ONEOF_HANDLER(bool,   bool)
+DEFINE_ONEOF_HANDLER(int32,  int32_t)
+DEFINE_ONEOF_HANDLER(uint32, uint32_t)
+DEFINE_ONEOF_HANDLER(float,  float)
+DEFINE_ONEOF_HANDLER(int64,  int64_t)
+DEFINE_ONEOF_HANDLER(uint64, uint64_t)
+DEFINE_ONEOF_HANDLER(double, double)
+
+#undef DEFINE_ONEOF_HANDLER
+
+// Handlers for strings in a oneof.
+static void *oneofstr_handler(void *closure,
+                              const void *hd,
+                              size_t size_hint) {
+  MessageHeader* msg = closure;
+  const oneof_handlerdata_t *oneofdata = hd;
+
+  DEREF(msg, oneofdata->case_ofs, uint32_t) =
+      oneofdata->oneof_case_num;
+  DEREF(msg, oneofdata->ofs, zval**) =
+      &(msg->std.properties_table)[oneofdata->property_ofs];
+
+  return empty_php_string(DEREF(msg, oneofdata->ofs, zval**));
+}
+
+static void *oneofbytes_handler(void *closure,
+                                const void *hd,
+                                size_t size_hint) {
+  MessageHeader* msg = closure;
+  const oneof_handlerdata_t *oneofdata = hd;
+
+  DEREF(msg, oneofdata->case_ofs, uint32_t) =
+      oneofdata->oneof_case_num;
+  DEREF(msg, oneofdata->ofs, zval**) =
+      &(msg->std.properties_table)[oneofdata->property_ofs];
+
+  // TODO(teboring): Add it back.
+  // rb_enc_associate(str, kRubyString8bitEncoding);
+
+  SEPARATE_ZVAL_IF_NOT_REF(DEREF(msg, oneofdata->ofs, zval**));
+  zval* str = *DEREF(msg, oneofdata->ofs, zval**);
+  zval_dtor(str);
+  ZVAL_STRINGL(str, "", 0, 1);
+  return (void*)str;
+}
+
+// Handler for a submessage field in a oneof.
+static void *oneofsubmsg_handler(void *closure,
+                                 const void *hd) {
+  MessageHeader* msg = closure;
+  const oneof_handlerdata_t *oneofdata = hd;
+  uint32_t oldcase = DEREF(msg, oneofdata->case_ofs, uint32_t);
+  zval* subdesc_php = get_def_obj((void*)oneofdata->md);
+  Descriptor* subdesc = zend_object_store_get_object(subdesc_php TSRMLS_CC);
+  zend_class_entry* subklass = subdesc->klass;
+  zval* submsg_php;
+  MessageHeader* submsg;
+
+  if (oldcase != oneofdata->oneof_case_num) {
+    DEREF(msg, oneofdata->ofs, zval**) =
+        &(msg->std.properties_table)[oneofdata->property_ofs];
+  }
+
+  if (Z_TYPE_P(*DEREF(msg, oneofdata->ofs, zval**)) == IS_NULL) {
+    zval* val = NULL;
+    MAKE_STD_ZVAL(val);
+    Z_TYPE_P(val) = IS_OBJECT;
+    Z_OBJVAL_P(val) = subklass->create_object(subklass TSRMLS_CC);
+
+    zval_ptr_dtor(DEREF(msg, oneofdata->ofs, zval**));
+    *DEREF(msg, oneofdata->ofs, zval**) = val;
+  }
+
+  DEREF(msg, oneofdata->case_ofs, uint32_t) =
+      oneofdata->oneof_case_num;
+
+  submsg_php = *DEREF(msg, oneofdata->ofs, zval**);
+  submsg = zend_object_store_get_object(submsg_php TSRMLS_CC);
+  return submsg;
+}
+
+// Set up handlers for a repeated field.
+static void add_handlers_for_repeated_field(upb_handlers *h,
+                                            const upb_fielddef *f,
+                                            size_t offset) {
+  upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER;
+  upb_handlerattr_sethandlerdata(&attr, newhandlerdata(h, offset));
+  upb_handlers_setstartseq(h, f, startseq_handler, &attr);
+  upb_handlerattr_uninit(&attr);
+
+  switch (upb_fielddef_type(f)) {
+
+#define SET_HANDLER(utype, ltype)                                 \
+  case utype:                                                     \
+    upb_handlers_set##ltype(h, f, append##ltype##_handler, NULL); \
+    break;
+
+    SET_HANDLER(UPB_TYPE_BOOL,   bool);
+    SET_HANDLER(UPB_TYPE_INT32,  int32);
+    SET_HANDLER(UPB_TYPE_UINT32, uint32);
+    SET_HANDLER(UPB_TYPE_ENUM,   int32);
+    SET_HANDLER(UPB_TYPE_FLOAT,  float);
+    SET_HANDLER(UPB_TYPE_INT64,  int64);
+    SET_HANDLER(UPB_TYPE_UINT64, uint64);
+    SET_HANDLER(UPB_TYPE_DOUBLE, double);
+
+#undef SET_HANDLER
+
+    case UPB_TYPE_STRING:
+    case UPB_TYPE_BYTES: {
+      bool is_bytes = upb_fielddef_type(f) == UPB_TYPE_BYTES;
+      upb_handlers_setstartstr(h, f, is_bytes ?
+                               appendbytes_handler : appendstr_handler,
+                               NULL);
+      upb_handlers_setstring(h, f, stringdata_handler, NULL);
+      break;
+    }
+    case UPB_TYPE_MESSAGE: {
+      upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER;
+      upb_handlerattr_sethandlerdata(&attr, newsubmsghandlerdata(h, 0, f));
+      upb_handlers_setstartsubmsg(h, f, appendsubmsg_handler, &attr);
+      upb_handlerattr_uninit(&attr);
+      break;
+    }
+  }
+}
+
+// Set up handlers for a singular field.
+static void add_handlers_for_singular_field(upb_handlers *h,
+                                            const upb_fielddef *f,
+                                            size_t offset) {
+  switch (upb_fielddef_type(f)) {
+    case UPB_TYPE_BOOL:
+    case UPB_TYPE_INT32:
+    case UPB_TYPE_UINT32:
+    case UPB_TYPE_ENUM:
+    case UPB_TYPE_FLOAT:
+    case UPB_TYPE_INT64:
+    case UPB_TYPE_UINT64:
+    case UPB_TYPE_DOUBLE:
+      upb_shim_set(h, f, offset, -1);
+      break;
+    case UPB_TYPE_STRING:
+    case UPB_TYPE_BYTES: {
+      bool is_bytes = upb_fielddef_type(f) == UPB_TYPE_BYTES;
+      upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER;
+      upb_handlerattr_sethandlerdata(&attr, newhandlerdata(h, offset));
+      upb_handlers_setstartstr(h, f,
+                               is_bytes ? bytes_handler : str_handler,
+                               &attr);
+      upb_handlers_setstring(h, f, stringdata_handler, &attr);
+      upb_handlerattr_uninit(&attr);
+      break;
+    }
+    case UPB_TYPE_MESSAGE: {
+      upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER;
+      upb_handlerattr_sethandlerdata(&attr, newsubmsghandlerdata(h, offset, f));
+      upb_handlers_setstartsubmsg(h, f, submsg_handler, &attr);
+      upb_handlerattr_uninit(&attr);
+      break;
+    }
+  }
+}
+
+// Adds handlers to a map field.
+static void add_handlers_for_mapfield(upb_handlers* h,
+                                      const upb_fielddef* fielddef,
+                                      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);
+  upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER;
+
+  upb_handlers_addcleanup(h, hd, free);
+  upb_handlerattr_sethandlerdata(&attr, hd);
+  upb_handlers_setstartsubmsg(h, fielddef, startmapentry_handler, &attr);
+  upb_handlerattr_uninit(&attr);
+}
+
+// Adds handlers to a map-entry msgdef.
+static void add_handlers_for_mapentry(const upb_msgdef* msgdef, upb_handlers* h,
+                                      Descriptor* desc) {
+  const upb_fielddef* key_field = map_entry_key(msgdef);
+  const upb_fielddef* value_field = map_entry_value(msgdef);
+  map_handlerdata_t* hd = new_map_handlerdata(0, msgdef, desc);
+  upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER;
+
+  upb_handlers_addcleanup(h, hd, free);
+  upb_handlerattr_sethandlerdata(&attr, hd);
+  upb_handlers_setendmsg(h, endmap_handler, &attr);
+
+  add_handlers_for_singular_field(h, key_field,
+                                  offsetof(map_parse_frame_t, key_storage));
+  add_handlers_for_singular_field(h, value_field,
+                                  offsetof(map_parse_frame_t, value_storage));
+}
+
+// Set up handlers for a oneof field.
+static void add_handlers_for_oneof_field(upb_handlers *h,
+                                         const upb_fielddef *f,
+                                         size_t offset,
+                                         size_t oneof_case_offset,
+                                         int property_cache_offset) {
+
+  upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER;
+  upb_handlerattr_sethandlerdata(
+      &attr, newoneofhandlerdata(h, offset, oneof_case_offset,
+                                 property_cache_offset, f));
+
+  switch (upb_fielddef_type(f)) {
+
+#define SET_HANDLER(utype, ltype)                                 \
+  case utype:                                                     \
+    upb_handlers_set##ltype(h, f, oneof##ltype##_handler, &attr); \
+    break;
+
+    SET_HANDLER(UPB_TYPE_BOOL,   bool);
+    SET_HANDLER(UPB_TYPE_INT32,  int32);
+    SET_HANDLER(UPB_TYPE_UINT32, uint32);
+    SET_HANDLER(UPB_TYPE_ENUM,   int32);
+    SET_HANDLER(UPB_TYPE_FLOAT,  float);
+    SET_HANDLER(UPB_TYPE_INT64,  int64);
+    SET_HANDLER(UPB_TYPE_UINT64, uint64);
+    SET_HANDLER(UPB_TYPE_DOUBLE, double);
+
+#undef SET_HANDLER
+
+    case UPB_TYPE_STRING:
+    case UPB_TYPE_BYTES: {
+      bool is_bytes = upb_fielddef_type(f) == UPB_TYPE_BYTES;
+      upb_handlers_setstartstr(h, f, is_bytes ?
+                               oneofbytes_handler : oneofstr_handler,
+                               &attr);
+      upb_handlers_setstring(h, f, stringdata_handler, NULL);
+      break;
+    }
+    case UPB_TYPE_MESSAGE: {
+      upb_handlers_setstartsubmsg(h, f, oneofsubmsg_handler, &attr);
+      break;
+    }
+  }
+
+  upb_handlerattr_uninit(&attr);
+}
+
+static void add_handlers_for_message(const void *closure, upb_handlers *h) {
+  const upb_msgdef* msgdef = upb_handlers_msgdef(h);
+  Descriptor* desc = (Descriptor*)zend_object_store_get_object(
+      get_def_obj((void*)msgdef) TSRMLS_CC);
+  upb_msg_field_iter i;
+
+  // If this is a mapentry message type, set up a special set of handlers and
+  // bail out of the normal (user-defined) message type handling.
+  if (upb_msgdef_mapentry(msgdef)) {
+    add_handlers_for_mapentry(msgdef, h, desc);
+    return;
+  }
+
+  // Ensure layout exists. We may be invoked to create handlers for a given
+  // message if we are included as a submsg of another message type before our
+  // class is actually built, so to work around this, we just create the layout
+  // (and handlers, in the class-building function) on-demand.
+  if (desc->layout == NULL) {
+    desc->layout = create_layout(desc->msgdef);
+  }
+
+  for (upb_msg_field_begin(&i, desc->msgdef);
+       !upb_msg_field_done(&i);
+       upb_msg_field_next(&i)) {
+    const upb_fielddef *f = upb_msg_iter_field(&i);
+    size_t offset = desc->layout->fields[upb_fielddef_index(f)].offset +
+        sizeof(MessageHeader);
+
+    if (upb_fielddef_containingoneof(f)) {
+      size_t oneof_case_offset =
+          desc->layout->fields[upb_fielddef_index(f)].case_offset +
+          sizeof(MessageHeader);
+      int property_cache_index =
+          desc->layout->fields[upb_fielddef_index(f)].cache_index;
+      add_handlers_for_oneof_field(h, f, offset, oneof_case_offset,
+                                   property_cache_index);
+    } else if (is_map_field(f)) {
+      add_handlers_for_mapfield(h, f, offset, desc);
+    } else if (upb_fielddef_isseq(f)) {
+      add_handlers_for_repeated_field(h, f, offset);
+    } else {
+      add_handlers_for_singular_field(h, f, offset);
+    }
+  }
+}
+
+// Creates upb handlers for populating a message.
+static const upb_handlers *new_fill_handlers(Descriptor* desc,
+                                             const void* owner) {
+  // TODO(cfallin, haberman): once upb gets a caching/memoization layer for
+  // handlers, reuse subdef handlers so that e.g. if we already parse
+  // B-with-field-of-type-C, we don't have to rebuild the whole hierarchy to
+  // parse A-with-field-of-type-B-with-field-of-type-C.
+  return upb_handlers_newfrozen(desc->msgdef, owner,
+                                add_handlers_for_message, NULL);
+}
+
+// Constructs the handlers for filling a message's data into an in-memory
+// object.
+const upb_handlers* get_fill_handlers(Descriptor* desc) {
+  if (!desc->fill_handlers) {
+    desc->fill_handlers =
+        new_fill_handlers(desc, &desc->fill_handlers);
+  }
+  return desc->fill_handlers;
+}
+
+const upb_pbdecodermethod *new_fillmsg_decodermethod(Descriptor* desc,
+                                                     const void* owner) {
+  const upb_handlers* handlers = get_fill_handlers(desc);
+  upb_pbdecodermethodopts opts;
+  upb_pbdecodermethodopts_init(&opts, handlers);
+
+  return upb_pbdecodermethod_new(&opts, owner);
+}
+
+static const upb_pbdecodermethod *msgdef_decodermethod(Descriptor* desc) {
+  if (desc->fill_method == NULL) {
+    desc->fill_method = new_fillmsg_decodermethod(
+        desc, &desc->fill_method);
+  }
+  return desc->fill_method;
+}
+
+// -----------------------------------------------------------------------------
+// Serializing.
+// -----------------------------------------------------------------------------
+
+static void putmsg(zval* msg, const Descriptor* desc, upb_sink* sink,
+                   int depth);
+
+static void putstr(zval* str, const upb_fielddef* f, upb_sink* sink);
+
+static void putrawstr(const char* str, int len, const upb_fielddef* f,
+                      upb_sink* sink);
+
+static void putsubmsg(zval* submsg, const upb_fielddef* f, upb_sink* sink,
+                      int depth);
+
+static void putarray(zval* array, const upb_fielddef* f, upb_sink* sink,
+                     int depth);
+static void putmap(zval* map, const upb_fielddef* f, upb_sink* sink, int depth);
+
+static upb_selector_t getsel(const upb_fielddef* f, upb_handlertype_t type) {
+  upb_selector_t ret;
+  bool ok = upb_handlers_getselector(f, type, &ret);
+  UPB_ASSERT(ok);
+  return ret;
+}
+
+static void put_optional_value(void* memory, int len, const upb_fielddef* f,
+                               int depth, upb_sink* sink) {
+  assert(upb_fielddef_label(f) == UPB_LABEL_OPTIONAL);
+
+  switch (upb_fielddef_type(f)) {
+#define T(upbtypeconst, upbtype, ctype, default_value)                         \
+  case upbtypeconst: {                                                         \
+    ctype value = DEREF(memory, 0, ctype);                                     \
+    if (value != default_value) {                                              \
+      upb_selector_t sel = getsel(f, upb_handlers_getprimitivehandlertype(f)); \
+      upb_sink_put##upbtype(sink, sel, value);                                 \
+    }                                                                          \
+  } break;
+
+    T(UPB_TYPE_FLOAT, float, float, 0.0)
+    T(UPB_TYPE_DOUBLE, double, double, 0.0)
+    T(UPB_TYPE_BOOL, bool, uint8_t, 0)
+    T(UPB_TYPE_ENUM, int32, int32_t, 0)
+    T(UPB_TYPE_INT32, int32, int32_t, 0)
+    T(UPB_TYPE_UINT32, uint32, uint32_t, 0)
+    T(UPB_TYPE_INT64, int64, int64_t, 0)
+    T(UPB_TYPE_UINT64, uint64, uint64_t, 0)
+
+#undef T
+    case UPB_TYPE_STRING:
+    case UPB_TYPE_BYTES:
+      putrawstr(memory, len, f, sink);
+      break;
+    case UPB_TYPE_MESSAGE: {
+      zval* submsg = *(zval**)memory;
+      putsubmsg(submsg, f, sink, depth);
+      break;
+    }
+    default:
+      assert(false);
+  }
+}
+
+// Only string/bytes fields are stored as zval.
+static const char* raw_value(void* memory, const upb_fielddef* f) {
+  switch (upb_fielddef_type(f)) {
+    case UPB_TYPE_STRING:
+    case UPB_TYPE_BYTES:
+      return Z_STRVAL_PP((zval**)memory);
+      break;
+    default:
+      return memory;
+  }
+}
+
+static int raw_value_len(void* memory, int len, const upb_fielddef* f) {
+  switch (upb_fielddef_type(f)) {
+    case UPB_TYPE_STRING:
+    case UPB_TYPE_BYTES:
+      return Z_STRLEN_PP((zval**)memory);
+      break;
+    default:
+      return len;
+  }
+}
+
+static void putmap(zval* map, const upb_fielddef* f, upb_sink* sink,
+                   int depth) {
+  Map* self;
+  upb_sink subsink;
+  const upb_fielddef* key_field;
+  const upb_fielddef* value_field;
+  MapIter it;
+  int len;
+
+  if (map == NULL) return;
+  self = UNBOX(Map, map);
+
+  upb_sink_startseq(sink, getsel(f, UPB_HANDLER_STARTSEQ), &subsink);
+
+  assert(upb_fielddef_type(f) == UPB_TYPE_MESSAGE);
+  key_field = map_field_key(f);
+  value_field = map_field_value(f);
+
+  for (map_begin(map, &it); !map_done(&it); map_next(&it)) {
+    upb_status status;
+
+    upb_sink entry_sink;
+    upb_sink_startsubmsg(&subsink, getsel(f, UPB_HANDLER_STARTSUBMSG),
+                         &entry_sink);
+    upb_sink_startmsg(&entry_sink);
+
+    // Serialize key.
+    const char *key = map_iter_key(&it, &len);
+    put_optional_value(key, len, key_field, depth + 1, &entry_sink);
+
+    // Serialize value.
+    upb_value value = map_iter_value(&it, &len);
+    put_optional_value(raw_value(upb_value_memory(&value), value_field),
+                       raw_value_len(upb_value_memory(&value), len, value_field),
+                       value_field, depth + 1, &entry_sink);
+
+    upb_sink_endmsg(&entry_sink, &status);
+    upb_sink_endsubmsg(&subsink, getsel(f, UPB_HANDLER_ENDSUBMSG));
+  }
+
+  upb_sink_endseq(sink, getsel(f, UPB_HANDLER_ENDSEQ));
+}
+
+static void putmsg(zval* msg_php, const Descriptor* desc, upb_sink* sink,
+                   int depth) {
+  upb_msg_field_iter i;
+  upb_status status;
+
+  upb_sink_startmsg(sink);
+
+  // Protect against cycles (possible because users may freely reassign message
+  // and repeated fields) by imposing a maximum recursion depth.
+  if (depth > ENCODE_MAX_NESTING) {
+    zend_error(E_ERROR,
+             "Maximum recursion depth exceeded during encoding.");
+  }
+
+  MessageHeader* msg = zend_object_store_get_object(msg_php TSRMLS_CC);
+
+  for (upb_msg_field_begin(&i, desc->msgdef); !upb_msg_field_done(&i);
+       upb_msg_field_next(&i)) {
+    upb_fielddef* f = upb_msg_iter_field(&i);
+    uint32_t offset = desc->layout->fields[upb_fielddef_index(f)].offset +
+                      sizeof(MessageHeader);
+
+    if (upb_fielddef_containingoneof(f)) {
+      uint32_t oneof_case_offset =
+          desc->layout->fields[upb_fielddef_index(f)].case_offset +
+          sizeof(MessageHeader);
+      // For a oneof, check that this field is actually present -- skip all the
+      // below if not.
+      if (DEREF(msg, oneof_case_offset, uint32_t) != upb_fielddef_number(f)) {
+        continue;
+      }
+      // Otherwise, fall through to the appropriate singular-field handler
+      // below.
+    }
+
+    if (is_map_field(f)) {
+      zval* map = *DEREF(msg, offset, zval**);
+      if (map != NULL) {
+        putmap(map, f, sink, depth);
+      }
+    } else if (upb_fielddef_isseq(f)) {
+      zval* array = *DEREF(msg, offset, zval**);
+      if (array != NULL) {
+        putarray(array, f, sink, depth);
+      }
+    } else if (upb_fielddef_isstring(f)) {
+      zval* str = *DEREF(msg, offset, zval**);
+      if (Z_STRLEN_P(str) > 0) {
+        putstr(str, f, sink);
+      }
+    } else if (upb_fielddef_issubmsg(f)) {
+      putsubmsg(*DEREF(msg, offset, zval**), f, sink, depth);
+    } else {
+      upb_selector_t sel = getsel(f, upb_handlers_getprimitivehandlertype(f));
+
+#define T(upbtypeconst, upbtype, ctype, default_value) \
+  case upbtypeconst: {                                 \
+    ctype value = DEREF(msg, offset, ctype);           \
+    if (value != default_value) {                      \
+      upb_sink_put##upbtype(sink, sel, value);         \
+    }                                                  \
+  } break;
+
+      switch (upb_fielddef_type(f)) {
+        T(UPB_TYPE_FLOAT, float, float, 0.0)
+        T(UPB_TYPE_DOUBLE, double, double, 0.0)
+        T(UPB_TYPE_BOOL, bool, uint8_t, 0)
+        case UPB_TYPE_ENUM:
+          T(UPB_TYPE_INT32, int32, int32_t, 0)
+          T(UPB_TYPE_UINT32, uint32, uint32_t, 0)
+          T(UPB_TYPE_INT64, int64, int64_t, 0)
+          T(UPB_TYPE_UINT64, uint64, uint64_t, 0)
+
+        case UPB_TYPE_STRING:
+        case UPB_TYPE_BYTES:
+        case UPB_TYPE_MESSAGE:
+          zend_error(E_ERROR, "Internal error.");
+      }
+
+#undef T
+    }
+  }
+
+  upb_sink_endmsg(sink, &status);
+}
+
+static void putstr(zval* str, const upb_fielddef *f, upb_sink *sink) {
+  upb_sink subsink;
+
+  if (ZVAL_IS_NULL(str)) return;
+
+  assert(Z_TYPE_P(str) == IS_STRING);
+
+  // Ensure that the string has the correct encoding. We also check at field-set
+  // time, but the user may have mutated the string object since then.
+  if (upb_fielddef_type(f) == UPB_TYPE_STRING &&
+      !is_structurally_valid_utf8(Z_STRVAL_P(str), Z_STRLEN_P(str))) {
+    zend_error(E_USER_ERROR, "Given string is not UTF8 encoded.");
+    return;
+  }
+
+  upb_sink_startstr(sink, getsel(f, UPB_HANDLER_STARTSTR), Z_STRLEN_P(str),
+                    &subsink);
+  upb_sink_putstring(&subsink, getsel(f, UPB_HANDLER_STRING), Z_STRVAL_P(str),
+                     Z_STRLEN_P(str), NULL);
+  upb_sink_endstr(sink, getsel(f, UPB_HANDLER_ENDSTR));
+}
+
+static void putrawstr(const char* str, int len, const upb_fielddef* f,
+                      upb_sink* sink) {
+  upb_sink subsink;
+
+  if (len == 0) return;
+
+  // Ensure that the string has the correct encoding. We also check at field-set
+  // time, but the user may have mutated the string object since then.
+  if (upb_fielddef_type(f) == UPB_TYPE_STRING &&
+      !is_structurally_valid_utf8(str, len)) {
+    zend_error(E_USER_ERROR, "Given string is not UTF8 encoded.");
+    return;
+  }
+
+  upb_sink_startstr(sink, getsel(f, UPB_HANDLER_STARTSTR), len, &subsink);
+  upb_sink_putstring(&subsink, getsel(f, UPB_HANDLER_STRING), str, len, NULL);
+  upb_sink_endstr(sink, getsel(f, UPB_HANDLER_ENDSTR));
+}
+
+static void putsubmsg(zval* submsg, const upb_fielddef* f, upb_sink* sink,
+                      int depth) {
+  upb_sink subsink;
+
+  if (Z_TYPE_P(submsg) == IS_NULL) return;
+
+  zval* php_descriptor = get_def_obj(upb_fielddef_msgsubdef(f));
+  Descriptor* subdesc =
+      (Descriptor*)zend_object_store_get_object(php_descriptor TSRMLS_CC);
+
+  upb_sink_startsubmsg(sink, getsel(f, UPB_HANDLER_STARTSUBMSG), &subsink);
+  putmsg(submsg, subdesc, &subsink, depth + 1);
+  upb_sink_endsubmsg(sink, getsel(f, UPB_HANDLER_ENDSUBMSG));
+}
+
+static void putarray(zval* array, const upb_fielddef* f, upb_sink* sink,
+                     int depth) {
+  upb_sink subsink;
+  upb_fieldtype_t type = upb_fielddef_type(f);
+  upb_selector_t sel = 0;
+  int size, i;
+
+  assert(array != NULL);
+  RepeatedField* intern =
+      (RepeatedField*)zend_object_store_get_object(array TSRMLS_CC);
+  size = zend_hash_num_elements(HASH_OF(intern->array));
+  if (size == 0) return;
+
+  upb_sink_startseq(sink, getsel(f, UPB_HANDLER_STARTSEQ), &subsink);
+
+  if (upb_fielddef_isprimitive(f)) {
+    sel = getsel(f, upb_handlers_getprimitivehandlertype(f));
+  }
+
+  for (i = 0; i < size; i++) {
+    void* memory = repeated_field_index_native(intern, i);
+    switch (type) {
+#define T(upbtypeconst, upbtype, ctype)                      \
+  case upbtypeconst:                                         \
+    upb_sink_put##upbtype(&subsink, sel, *((ctype*)memory)); \
+    break;
+
+      T(UPB_TYPE_FLOAT, float, float)
+      T(UPB_TYPE_DOUBLE, double, double)
+      T(UPB_TYPE_BOOL, bool, int8_t)
+      case UPB_TYPE_ENUM:
+        T(UPB_TYPE_INT32, int32, int32_t)
+        T(UPB_TYPE_UINT32, uint32, uint32_t)
+        T(UPB_TYPE_INT64, int64, int64_t)
+        T(UPB_TYPE_UINT64, uint64, uint64_t)
+
+      case UPB_TYPE_STRING:
+      case UPB_TYPE_BYTES:
+        putstr(*((zval**)memory), f, &subsink);
+        break;
+      case UPB_TYPE_MESSAGE:
+        putsubmsg(*((zval**)memory), f, &subsink, depth);
+        break;
+
+#undef T
+    }
+  }
+  upb_sink_endseq(sink, getsel(f, UPB_HANDLER_ENDSEQ));
+}
+
+static const upb_handlers* msgdef_pb_serialize_handlers(Descriptor* desc) {
+  if (desc->pb_serialize_handlers == NULL) {
+    desc->pb_serialize_handlers =
+        upb_pb_encoder_newhandlers(desc->msgdef, &desc->pb_serialize_handlers);
+  }
+  return desc->pb_serialize_handlers;
+}
+
+// -----------------------------------------------------------------------------
+// PHP encode/decode methods
+// -----------------------------------------------------------------------------
+
+PHP_METHOD(Message, encode) {
+  zval* php_descriptor = get_ce_obj(Z_OBJCE_P(getThis()));
+  Descriptor* desc =
+      (Descriptor*)zend_object_store_get_object(php_descriptor TSRMLS_CC);
+
+  stringsink sink;
+  stringsink_init(&sink);
+
+  {
+    const upb_handlers* serialize_handlers = msgdef_pb_serialize_handlers(desc);
+
+    stackenv se;
+    upb_pb_encoder* encoder;
+
+    stackenv_init(&se, "Error occurred during encoding: %s");
+    encoder = upb_pb_encoder_create(&se.env, serialize_handlers, &sink.sink);
+
+    putmsg(getThis(), desc, upb_pb_encoder_input(encoder), 0);
+
+    RETVAL_STRINGL(sink.ptr, sink.len, 1);
+
+    stackenv_uninit(&se);
+    stringsink_uninit(&sink);
+  }
+}
+
+PHP_METHOD(Message, decode) {
+  zval* php_descriptor = get_ce_obj(Z_OBJCE_P(getThis()));
+  Descriptor* desc =
+      (Descriptor*)zend_object_store_get_object(php_descriptor TSRMLS_CC);
+  MessageHeader* msg = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+  char *data = NULL;
+  int data_len;
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &data, &data_len) ==
+      FAILURE) {
+    return;
+  }
+
+  {
+    const upb_pbdecodermethod* method = msgdef_decodermethod(desc);
+    const upb_handlers* h = upb_pbdecodermethod_desthandlers(method);
+    stackenv se;
+    upb_sink sink;
+    upb_pbdecoder* decoder;
+    stackenv_init(&se, "Error occurred during parsing: %s");
+
+    upb_sink_reset(&sink, h, msg);
+    decoder = upb_pbdecoder_create(&se.env, method, &sink);
+    upb_bufsrc_putbuf(data, data_len, upb_pbdecoder_input(decoder));
+
+    stackenv_uninit(&se);
+  }
+}
diff --git a/php/ext/google/protobuf/map.c b/php/ext/google/protobuf/map.c
new file mode 100644
index 0000000..6d822ff
--- /dev/null
+++ b/php/ext/google/protobuf/map.c
@@ -0,0 +1,470 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <ext/spl/spl_iterators.h>
+#include <Zend/zend_API.h>
+#include <Zend/zend_interfaces.h>
+
+#include "protobuf.h"
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetGet, 0, 0, 1)
+  ZEND_ARG_INFO(0, index)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetSet, 0, 0, 2)
+  ZEND_ARG_INFO(0, index)
+  ZEND_ARG_INFO(0, newval)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_void, 0)
+ZEND_END_ARG_INFO()
+
+// Utilities
+
+void* upb_value_memory(upb_value* v) {
+  return (void*)(&v->val);
+}
+
+// -----------------------------------------------------------------------------
+// Basic map operations on top of upb's strtable.
+//
+// Note that we roll our own `Map` container here because, as for
+// `RepeatedField`, we want a strongly-typed container. This is so that any user
+// errors due to incorrect map key or value types are raised as close as
+// possible to the error site, rather than at some deferred point (e.g.,
+// serialization).
+//
+// We build our `Map` on top of upb_strtable so that we're able to take
+// advantage of the native_slot storage abstraction, as RepeatedField does.
+// (This is not quite a perfect mapping -- see the key conversions below -- but
+// gives us full support and error-checking for all value types for free.)
+// -----------------------------------------------------------------------------
+
+// Map values are stored using the native_slot abstraction (as with repeated
+// field values), but keys are a bit special. Since we use a strtable, we need
+// to store keys as sequences of bytes such that equality of those bytes maps
+// one-to-one to equality of keys. We store strings directly (i.e., they map to
+// their own bytes) and integers as native integers (using the native_slot
+// abstraction).
+
+// Note that there is another tradeoff here in keeping string keys as native
+// strings rather than PHP strings: traversing the Map requires conversion to
+// PHP string values on every traversal, potentially creating more garbage. We
+// should consider ways to cache a PHP version of the key if this becomes an
+// issue later.
+
+// Forms a key to use with the underlying strtable from a PHP key value. |buf|
+// must point to TABLE_KEY_BUF_LENGTH bytes of temporary space, used to
+// construct a key byte sequence if needed. |out_key| and |out_length| provide
+// the resulting key data/length.
+#define TABLE_KEY_BUF_LENGTH 8  // sizeof(uint64_t)
+static bool table_key(Map* self, zval* key,
+                      char* buf,
+                      const char** out_key,
+                      size_t* out_length) {
+  switch (self->key_type) {
+    case UPB_TYPE_STRING:
+      if (!protobuf_convert_to_string(key)) {
+        return false;
+      }
+      if (!is_structurally_valid_utf8(Z_STRVAL_P(key), Z_STRLEN_P(key))) {
+        zend_error(E_USER_ERROR, "Given key is not UTF8 encoded.");
+        return false;
+      }
+      *out_key = Z_STRVAL_P(key);
+      *out_length = Z_STRLEN_P(key);
+      break;
+
+#define CASE_TYPE(upb_type, type, c_type, php_type)                   \
+  case UPB_TYPE_##upb_type: {                                         \
+    c_type type##_value;                                              \
+    if (!protobuf_convert_to_##type(key, &type##_value)) {            \
+      return false;                                                   \
+    }                                                                 \
+    native_slot_set(self->key_type, NULL, buf, key);                  \
+    *out_key = buf;                                                   \
+    *out_length = native_slot_size(self->key_type);                   \
+    break;                                                            \
+  }
+      CASE_TYPE(BOOL, bool, int8_t, BOOL)
+      CASE_TYPE(INT32, int32, int32_t, LONG)
+      CASE_TYPE(INT64, int64, int64_t, LONG)
+      CASE_TYPE(UINT32, uint32, uint32_t, LONG)
+      CASE_TYPE(UINT64, uint64, uint64_t, LONG)
+
+#undef CASE_TYPE
+
+    default:
+      // Map constructor should not allow a Map with another key type to be
+      // constructed.
+      assert(false);
+      break;
+  }
+
+  return true;
+}
+
+// -----------------------------------------------------------------------------
+// MapField methods
+// -----------------------------------------------------------------------------
+
+static zend_function_entry map_field_methods[] = {
+  PHP_ME(MapField, __construct,  NULL,              ZEND_ACC_PUBLIC)
+  PHP_ME(MapField, offsetExists, arginfo_offsetGet, ZEND_ACC_PUBLIC)
+  PHP_ME(MapField, offsetGet,    arginfo_offsetGet, ZEND_ACC_PUBLIC)
+  PHP_ME(MapField, offsetSet,    arginfo_offsetSet, ZEND_ACC_PUBLIC)
+  PHP_ME(MapField, offsetUnset,  arginfo_offsetGet, ZEND_ACC_PUBLIC)
+  PHP_ME(MapField, count,        arginfo_void,      ZEND_ACC_PUBLIC)
+  ZEND_FE_END
+};
+
+// -----------------------------------------------------------------------------
+// MapField creation/desctruction
+// -----------------------------------------------------------------------------
+
+zend_class_entry* map_field_type;
+zend_object_handlers* map_field_handlers;
+
+static map_begin_internal(Map *map, MapIter *iter) {
+  iter->self = map;
+  upb_strtable_begin(&iter->it, &map->table);
+}
+
+static HashTable *map_field_get_gc(zval *object, zval ***table,
+                                        int *n TSRMLS_DC) {
+  // TODO(teboring): Unfortunately, zend engine does not support garbage
+  // collection for custom array. We have to use zend engine's native array
+  // instead.
+  *table = NULL;
+  *n = 0;
+  return NULL;
+}
+
+void map_field_init(TSRMLS_D) {
+  zend_class_entry class_type;
+  const char* class_name = "Google\\Protobuf\\Internal\\MapField";
+  INIT_CLASS_ENTRY_EX(class_type, class_name, strlen(class_name),
+                      map_field_methods);
+
+  map_field_type = zend_register_internal_class(&class_type TSRMLS_CC);
+  map_field_type->create_object = map_field_create;
+
+  zend_class_implements(map_field_type TSRMLS_CC, 2, spl_ce_ArrayAccess,
+                        spl_ce_Countable);
+
+  map_field_handlers = PEMALLOC(zend_object_handlers);
+  memcpy(map_field_handlers, zend_get_std_object_handlers(),
+         sizeof(zend_object_handlers));
+  map_field_handlers->get_gc = map_field_get_gc;
+}
+
+zend_object_value map_field_create(zend_class_entry *ce TSRMLS_DC) {
+  zend_object_value retval = {0};
+  Map *intern;
+
+  intern = emalloc(sizeof(Map));
+  memset(intern, 0, sizeof(Map));
+
+  zend_object_std_init(&intern->std, ce TSRMLS_CC);
+  object_properties_init(&intern->std, ce);
+
+  // Table value type is always UINT64: this ensures enough space to store the
+  // native_slot value.
+  if (!upb_strtable_init(&intern->table, UPB_CTYPE_UINT64)) {
+    zend_error(E_USER_ERROR, "Could not allocate table.");
+  }
+
+  retval.handle = zend_objects_store_put(
+      intern, (zend_objects_store_dtor_t)zend_objects_destroy_object,
+      (zend_objects_free_object_storage_t)map_field_free, NULL TSRMLS_CC);
+  retval.handlers = map_field_handlers;
+
+  return retval;
+}
+
+void map_field_free(void *object TSRMLS_DC) {
+  Map *map = (Map *)object;
+
+  switch (map->value_type) {
+    case UPB_TYPE_MESSAGE:
+    case UPB_TYPE_STRING:
+    case UPB_TYPE_BYTES: {
+      MapIter it;
+      int len;
+      for (map_begin_internal(map, &it); !map_done(&it); map_next(&it)) {
+        upb_value value = map_iter_value(&it, &len);
+        void *mem = upb_value_memory(&value);
+        zval_ptr_dtor(mem);
+      }
+      break;
+    }
+    default:
+      break;
+  }
+
+  upb_strtable_uninit(&map->table);
+  zend_object_std_dtor(&map->std TSRMLS_CC);
+  efree(object);
+}
+
+void map_field_create_with_type(zend_class_entry *ce, const upb_fielddef *field,
+                                zval **map_field TSRMLS_DC) {
+  MAKE_STD_ZVAL(*map_field);
+  Z_TYPE_PP(map_field) = IS_OBJECT;
+  Z_OBJVAL_PP(map_field) =
+      map_field_type->create_object(map_field_type TSRMLS_CC);
+
+  Map* intern =
+      (Map*)zend_object_store_get_object(*map_field TSRMLS_CC);
+
+  const upb_fielddef *key_field = map_field_key(field);
+  const upb_fielddef *value_field = map_field_value(field);
+  intern->key_type = upb_fielddef_type(key_field);
+  intern->value_type = upb_fielddef_type(value_field);
+  intern->msg_ce = field_type_class(value_field);
+}
+
+static void map_field_free_element(void *object) {
+}
+
+// -----------------------------------------------------------------------------
+// MapField Handlers
+// -----------------------------------------------------------------------------
+
+static bool *map_field_read_dimension(zval *object, zval *key, int type,
+                                      zval **retval TSRMLS_DC) {
+  Map *intern =
+      (Map *)zend_object_store_get_object(object TSRMLS_CC);
+
+  char keybuf[TABLE_KEY_BUF_LENGTH];
+  const char* keyval = NULL;
+  size_t length = 0;
+  upb_value v;
+#ifndef NDEBUG
+  v.ctype = UPB_CTYPE_UINT64;
+#endif
+  if (!table_key(intern, key, keybuf, &keyval, &length)) {
+    return false;
+  }
+
+  if (upb_strtable_lookup2(&intern->table, keyval, length, &v)) {
+    void* mem = upb_value_memory(&v);
+    native_slot_get(intern->value_type, mem, retval TSRMLS_CC);
+    return true;
+  } else {
+    zend_error(E_USER_ERROR, "Given key doesn't exist.");
+    return false;
+  }
+}
+
+bool map_index_set(Map *intern, const char* keyval, int length, upb_value v) {
+  // Replace any existing value by issuing a 'remove' operation first.
+  upb_strtable_remove2(&intern->table, keyval, length, NULL);
+  if (!upb_strtable_insert2(&intern->table, keyval, length, v)) {
+    zend_error(E_USER_ERROR, "Could not insert into table");
+    return false;
+  }
+  return true;
+}
+
+static bool map_field_write_dimension(zval *object, zval *key,
+                                      zval *value TSRMLS_DC) {
+  Map *intern = (Map *)zend_object_store_get_object(object TSRMLS_CC);
+
+  char keybuf[TABLE_KEY_BUF_LENGTH];
+  const char* keyval = NULL;
+  size_t length = 0;
+  upb_value v;
+  void* mem;
+  if (!table_key(intern, key, keybuf, &keyval, &length)) {
+    return false;
+  }
+
+  mem = upb_value_memory(&v);
+  memset(mem, 0, native_slot_size(intern->value_type));
+  if(!native_slot_set(intern->value_type, intern->msg_ce, mem, value)) {
+    return false;
+  }
+#ifndef NDEBUG
+  v.ctype = UPB_CTYPE_UINT64;
+#endif
+
+  // Replace any existing value by issuing a 'remove' operation first.
+  upb_strtable_remove2(&intern->table, keyval, length, NULL);
+  if (!upb_strtable_insert2(&intern->table, keyval, length, v)) {
+    zend_error(E_USER_ERROR, "Could not insert into table");
+    return false;
+  }
+
+  return true;
+}
+
+static bool map_field_unset_dimension(zval *object, zval *key TSRMLS_DC) {
+  Map *intern = (Map *)zend_object_store_get_object(object TSRMLS_CC);
+
+  char keybuf[TABLE_KEY_BUF_LENGTH];
+  const char* keyval = NULL;
+  size_t length = 0;
+  upb_value v;
+  if (!table_key(intern, key, keybuf, &keyval, &length)) {
+    return false;
+  }
+#ifndef NDEBUG
+  v.ctype = UPB_CTYPE_UINT64;
+#endif
+
+  upb_strtable_remove2(&intern->table, keyval, length, &v);
+
+  return true;
+}
+
+// -----------------------------------------------------------------------------
+// PHP MapField Methods
+// -----------------------------------------------------------------------------
+
+PHP_METHOD(MapField, __construct) {
+  long key_type, value_type;
+  zend_class_entry* klass = NULL;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll|C", &key_type,
+                            &value_type, &klass) == FAILURE) {
+    return;
+  }
+
+  Map* intern =
+      (Map*)zend_object_store_get_object(getThis() TSRMLS_CC);
+  intern->key_type = to_fieldtype(key_type);
+  intern->value_type = to_fieldtype(value_type);
+  intern->msg_ce = klass;
+
+  // Check that the key type is an allowed type.
+  switch (intern->key_type) {
+    case UPB_TYPE_INT32:
+    case UPB_TYPE_INT64:
+    case UPB_TYPE_UINT32:
+    case UPB_TYPE_UINT64:
+    case UPB_TYPE_BOOL:
+    case UPB_TYPE_STRING:
+    case UPB_TYPE_BYTES:
+      // These are OK.
+      break;
+    default:
+      zend_error(E_USER_ERROR, "Invalid key type for map.");
+  }
+}
+
+PHP_METHOD(MapField, offsetExists) {
+  zval *key;
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &key) ==
+      FAILURE) {
+    return;
+  }
+
+  Map *intern = (Map *)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+  char keybuf[TABLE_KEY_BUF_LENGTH];
+  const char* keyval = NULL;
+  size_t length = 0;
+  upb_value v;
+#ifndef NDEBUG
+  v.ctype = UPB_CTYPE_UINT64;
+#endif
+  if (!table_key(intern, key, keybuf, &keyval, &length)) {
+    return false;
+  }
+
+  RETURN_BOOL(upb_strtable_lookup2(&intern->table, keyval, length, &v));
+}
+
+PHP_METHOD(MapField, offsetGet) {
+  zval *index, *value;
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &index) ==
+      FAILURE) {
+    return;
+  }
+  map_field_read_dimension(getThis(), index, BP_VAR_R,
+                           return_value_ptr TSRMLS_CC);
+}
+
+PHP_METHOD(MapField, offsetSet) {
+  zval *index, *value;
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &index, &value) ==
+      FAILURE) {
+    return;
+  }
+  map_field_write_dimension(getThis(), index, value TSRMLS_CC);
+}
+
+PHP_METHOD(MapField, offsetUnset) {
+  zval *index;
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &index) ==
+      FAILURE) {
+    return;
+  }
+  map_field_unset_dimension(getThis(), index TSRMLS_CC);
+}
+
+PHP_METHOD(MapField, count) {
+  Map *intern =
+      (MapField *)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+  if (zend_parse_parameters_none() == FAILURE) {
+    return;
+  }
+
+  RETURN_LONG(upb_strtable_count(&intern->table));
+}
+
+// -----------------------------------------------------------------------------
+// Map Iterator
+// -----------------------------------------------------------------------------
+
+void map_begin(zval *map_php, MapIter *iter) {
+  Map *self = UNBOX(Map, map_php);
+  map_begin_internal(self, iter);
+}
+
+void map_next(MapIter *iter) {
+  upb_strtable_next(&iter->it);
+}
+
+bool map_done(MapIter *iter) {
+  return upb_strtable_done(&iter->it);
+}
+
+const char *map_iter_key(MapIter *iter, int *len) {
+  *len = upb_strtable_iter_keylength(&iter->it);
+  return upb_strtable_iter_key(&iter->it);
+}
+
+upb_value map_iter_value(MapIter *iter, int *len) {
+  *len = native_slot_size(iter->self->value_type);
+  return upb_strtable_iter_value(&iter->it);
+}
diff --git a/php/ext/google/protobuf/message.c b/php/ext/google/protobuf/message.c
index c062d66..537cd1c 100644
--- a/php/ext/google/protobuf/message.c
+++ b/php/ext/google/protobuf/message.c
@@ -29,245 +29,219 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <php.h>
+#include <stdlib.h>
 
 #include "protobuf.h"
 
+static zend_class_entry* message_type;
+zend_object_handlers* message_handlers;
+
+static  zend_function_entry message_methods[] = {
+  PHP_ME(Message, encode, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(Message, decode, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(Message, readOneof, NULL, ZEND_ACC_PROTECTED)
+  PHP_ME(Message, writeOneof, NULL, ZEND_ACC_PROTECTED)
+  {NULL, NULL, NULL}
+};
+
+// Forward declare static functions.
+
+static void message_set_property(zval* object, zval* member, zval* value,
+                                 const zend_literal* key TSRMLS_DC);
+static zval* message_get_property(zval* object, zval* member, int type,
+                                  const zend_literal* key TSRMLS_DC);
+static zval** message_get_property_ptr_ptr(zval* object, zval* member, int type,
+                                           const zend_literal* key TSRMLS_DC);
+
+static zend_object_value message_create(zend_class_entry* ce TSRMLS_DC);
+static void message_free(void* object TSRMLS_DC);
+
 // -----------------------------------------------------------------------------
-// Class/module creation from msgdefs and enumdefs, respectively.
+// PHP Message Handlers
 // -----------------------------------------------------------------------------
 
-void* message_data(void* msg) {
-  return ((uint8_t *)msg) + sizeof(MessageHeader);
+void message_init(TSRMLS_D) {
+  zend_class_entry class_type;
+  INIT_CLASS_ENTRY(class_type, "Google\\Protobuf\\Internal\\Message",
+                   message_methods);
+  message_type = zend_register_internal_class(&class_type TSRMLS_CC);
+
+  message_handlers = PEMALLOC(zend_object_handlers);
+  memcpy(message_handlers, zend_get_std_object_handlers(),
+         sizeof(zend_object_handlers));
+  message_handlers->write_property = message_set_property;
+  message_handlers->read_property = message_get_property;
+  message_handlers->get_property_ptr_ptr = message_get_property_ptr_ptr;
 }
 
-void message_set_property(zval* object, zval* field_name, zval* value,
-                          const zend_literal* key TSRMLS_DC) {
+static void message_set_property(zval* object, zval* member, zval* value,
+                                 const zend_literal* key TSRMLS_DC) {
+  if (Z_TYPE_P(member) != IS_STRING) {
+    zend_error(E_USER_ERROR, "Unexpected type for field name");
+    return;
+  }
+
+  if (Z_OBJCE_P(object) != EG(scope)) {
+    // User cannot set property directly (e.g., $m->a = 1)
+    zend_error(E_USER_ERROR, "Cannot access private property.");
+    return;
+  }
+
   const upb_fielddef* field;
 
   MessageHeader* self = zend_object_store_get_object(object TSRMLS_CC);
 
-  CHECK_TYPE(field_name, IS_STRING);
-  field = upb_msgdef_ntofz(self->descriptor->msgdef, Z_STRVAL_P(field_name));
+  field = upb_msgdef_ntofz(self->descriptor->msgdef, Z_STRVAL_P(member));
   if (field == NULL) {
-    zend_error(E_ERROR, "Unknown field: %s", Z_STRVAL_P(field_name));
+    zend_error(E_USER_ERROR, "Unknown field: %s", Z_STRVAL_P(member));
   }
-  layout_set(self->descriptor->layout, message_data(self), field, value);
+
+  layout_set(self->descriptor->layout, self, field, value);
 }
 
-zval* message_get_property(zval* object, zval* member, int type,
-                             const zend_literal* key TSRMLS_DC) {
+static zval* message_get_property(zval* object, zval* member, int type,
+                                  const zend_literal* key TSRMLS_DC) {
+  if (Z_TYPE_P(member) != IS_STRING) {
+    zend_error(E_USER_ERROR, "Property name has to be a string.");
+    return EG(uninitialized_zval_ptr);
+  }
+
+  if (Z_OBJCE_P(object) != EG(scope)) {
+    // User cannot get property directly (e.g., $a = $m->a)
+    zend_error(E_USER_ERROR, "Cannot access private property.");
+    return EG(uninitialized_zval_ptr);
+  }
+
+  zend_property_info* property_info = NULL;
+
+  // All properties should have been declared in the generated code and have
+  // corresponding zvals in properties_table.
+  ulong h = zend_get_hash_value(Z_STRVAL_P(member), Z_STRLEN_P(member) + 1);
+  if (zend_hash_quick_find(&Z_OBJCE_P(object)->properties_info,
+                           Z_STRVAL_P(member), Z_STRLEN_P(member) + 1, h,
+                           (void**)&property_info) != SUCCESS) {
+    zend_error(E_USER_ERROR, "Property does not exist.");
+    return EG(uninitialized_zval_ptr);
+  }
+
   MessageHeader* self =
       (MessageHeader*)zend_object_store_get_object(object TSRMLS_CC);
-  CHECK_TYPE(member, IS_STRING);
 
   const upb_fielddef* field;
   field = upb_msgdef_ntofz(self->descriptor->msgdef, Z_STRVAL_P(member));
   if (field == NULL) {
     return EG(uninitialized_zval_ptr);
   }
-  zval* retval = layout_get(self->descriptor->layout, message_data(self), field TSRMLS_CC);
-  return retval;
+  return layout_get(
+      self->descriptor->layout, message_data(self), field,
+      &Z_OBJ_P(object)->properties_table[property_info->offset] TSRMLS_CC);
 }
 
-static  zend_function_entry message_methods[] = {
-  PHP_ME(Message, encode, NULL, ZEND_ACC_PUBLIC)
-  {NULL, NULL, NULL}
-};
-
-/* stringsink *****************************************************************/
-
-// This should probably be factored into a common upb component.
-
-typedef struct {
-  upb_byteshandler handler;
-  upb_bytessink sink;
-  char *ptr;
-  size_t len, size;
-} stringsink;
-
-static void *stringsink_start(void *_sink, const void *hd, size_t size_hint) {
-  stringsink *sink = _sink;
-  sink->len = 0;
-  return sink;
-}
-
-static size_t stringsink_string(void *_sink, const void *hd, const char *ptr,
-                                size_t len, const upb_bufhandle *handle) {
-  stringsink *sink = _sink;
-  size_t new_size = sink->size;
-
-  UPB_UNUSED(hd);
-  UPB_UNUSED(handle);
-
-  while (sink->len + len > new_size) {
-    new_size *= 2;
-  }
-
-  if (new_size != sink->size) {
-    sink->ptr = realloc(sink->ptr, new_size);
-    sink->size = new_size;
-  }
-
-  memcpy(sink->ptr + sink->len, ptr, len);
-  sink->len += len;
-
-  return len;
-}
-
-void stringsink_init(stringsink *sink) {
-  upb_byteshandler_init(&sink->handler);
-  upb_byteshandler_setstartstr(&sink->handler, stringsink_start, NULL);
-  upb_byteshandler_setstring(&sink->handler, stringsink_string, NULL);
-
-  upb_bytessink_reset(&sink->sink, &sink->handler, sink);
-
-  sink->size = 32;
-  sink->ptr = malloc(sink->size);
-  sink->len = 0;
-}
-
-void stringsink_uninit(stringsink *sink) { free(sink->ptr); }
-
-// Stack-allocated context during an encode/decode operation. Contains the upb
-// environment and its stack-based allocator, an initial buffer for allocations
-// to avoid malloc() when possible, and a template for PHP exception messages
-// if any error occurs.
-#define STACK_ENV_STACKBYTES 4096
-typedef struct {
-  upb_env env;
-  upb_seededalloc alloc;
-  const char *php_error_template;
-  char allocbuf[STACK_ENV_STACKBYTES];
-} stackenv;
-
-static void stackenv_init(stackenv* se, const char* errmsg);
-static void stackenv_uninit(stackenv* se);
-
-// Callback invoked by upb if any error occurs during parsing or serialization.
-static bool env_error_func(void* ud, const upb_status* status) {
-    stackenv* se = ud;
-    // Free the env -- rb_raise will longjmp up the stack past the encode/decode
-    // function so it would not otherwise have been freed.
-    stackenv_uninit(se);
-
-    // TODO(teboring): have a way to verify that this is actually a parse error,
-    // instead of just throwing "parse error" unconditionally.
-    zend_error(E_ERROR, se->php_error_template);
-    // Never reached.
-    return false;
-}
-
-static void stackenv_init(stackenv* se, const char* errmsg) {
-  se->php_error_template = errmsg;
-  upb_env_init(&se->env);
-  upb_seededalloc_init(&se->alloc, &se->allocbuf, STACK_ENV_STACKBYTES);
-  upb_env_setallocfunc(&se->env, upb_seededalloc_getallocfunc(&se->alloc),
-                       &se->alloc);
-  upb_env_seterrorfunc(&se->env, env_error_func, se);
-}
-
-static void stackenv_uninit(stackenv* se) {
-  upb_env_uninit(&se->env);
-  upb_seededalloc_uninit(&se->alloc);
+static zval** message_get_property_ptr_ptr(zval* object, zval* member, int type,
+                                           const zend_literal* key TSRMLS_DC) {
+  return NULL;
 }
 
 // -----------------------------------------------------------------------------
-// Message
+// C Message Utilities
 // -----------------------------------------------------------------------------
 
-static const upb_handlers* msgdef_pb_serialize_handlers(Descriptor* desc) {
-  if (desc->pb_serialize_handlers == NULL) {
-    desc->pb_serialize_handlers =
-        upb_pb_encoder_newhandlers(desc->msgdef, &desc->pb_serialize_handlers);
+void* message_data(void* msg) {
+  return ((uint8_t*)msg) + sizeof(MessageHeader);
+}
+
+static void message_free(void* object TSRMLS_DC) {
+  MessageHeader* msg = (MessageHeader*)object;
+  int i;
+
+  for (i = 0; i < msg->std.ce->default_properties_count; i++) {
+    zval_ptr_dtor(&msg->std.properties_table[i]);
   }
-  return desc->pb_serialize_handlers;
+  efree(msg->std.properties_table);
+  efree(msg);
 }
 
-PHP_METHOD(Message, encode) {
-  Descriptor* desc = (Descriptor*)zend_object_store_get_object(
-      CE_STATIC_MEMBERS(Z_OBJCE_P(getThis()))[0] TSRMLS_CC);
-
-  stringsink sink;
-  stringsink_init(&sink);
-
-  {
-    const upb_handlers* serialize_handlers = msgdef_pb_serialize_handlers(desc);
-
-    stackenv se;
-    upb_pb_encoder* encoder;
-
-    stackenv_init(&se, "Error occurred during encoding: %s");
-    encoder = upb_pb_encoder_create(&se.env, serialize_handlers, &sink.sink);
-
-    putmsg(getThis(), desc, upb_pb_encoder_input(encoder), 0);
-
-    RETVAL_STRINGL(sink.ptr, sink.len, 1);
-
-    stackenv_uninit(&se);
-    stringsink_uninit(&sink);
-  }
-}
-
-void message_free(void * object TSRMLS_DC) {
-  FREE(object);
-}
-
-zend_object_value message_create(zend_class_entry* ce TSRMLS_DC) {
+static zend_object_value message_create(zend_class_entry* ce TSRMLS_DC) {
   zend_object_value return_value;
 
-  zval* php_descriptor = get_def_obj(ce);
+  zval* php_descriptor = get_ce_obj(ce);
 
   Descriptor* desc = zend_object_store_get_object(php_descriptor TSRMLS_CC);
   MessageHeader* msg = (MessageHeader*)ALLOC_N(
       uint8_t, sizeof(MessageHeader) + desc->layout->size);
   memset(message_data(msg), 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().
+  // We wrap first so that everything in the message object is GC-rooted in
+  // case a collection happens during object creation in layout_init().
   msg->descriptor = desc;
 
-  layout_init(desc->layout, message_data(msg));
   zend_object_std_init(&msg->std, ce TSRMLS_CC);
+  object_properties_init(&msg->std, ce);
+  layout_init(desc->layout, message_data(msg), msg->std.properties_table);
 
   return_value.handle = zend_objects_store_put(
-      msg, (zend_objects_store_dtor_t)zend_objects_destroy_object,
-      message_free, NULL TSRMLS_CC);
+      msg, (zend_objects_store_dtor_t)zend_objects_destroy_object, message_free,
+      NULL TSRMLS_CC);
 
-  return_value.handlers = PROTOBUF_G(message_handlers);
+  return_value.handlers = message_handlers;
   return return_value;
 }
 
-const zend_class_entry* build_class_from_descriptor(
-    zval* php_descriptor TSRMLS_DC) {
-  Descriptor* desc = php_to_descriptor(php_descriptor);
+void build_class_from_descriptor(zval* php_descriptor TSRMLS_DC) {
+  Descriptor* desc = UNBOX(Descriptor, php_descriptor);
+
+  // Map entries don't have existing php class.
+  if (upb_msgdef_mapentry(desc->msgdef)) {
+    return;
+  }
+
+  zend_class_entry* registered_ce = desc->klass;
+
   if (desc->layout == NULL) {
     MessageLayout* layout = create_layout(desc->msgdef);
     desc->layout = layout;
   }
-  // TODO(teboring): Add it back.
-  // if (desc->fill_method == NULL) {
-  //   desc->fill_method = new_fillmsg_decodermethod(desc, &desc->fill_method);
-  // }
-
-  const char* name = upb_msgdef_fullname(desc->msgdef);
-  if (name == NULL) {
-    zend_error(E_ERROR, "Descriptor does not have assigned name.");
-  }
-
-  zend_class_entry class_entry;
-  INIT_CLASS_ENTRY_EX(class_entry, name, strlen(name), message_methods);
-  zend_class_entry* registered_ce =
-      zend_register_internal_class(&class_entry TSRMLS_CC);
-
-  add_def_obj(registered_ce, php_descriptor);
-
-  if (PROTOBUF_G(message_handlers) == NULL) {
-    PROTOBUF_G(message_handlers) = ALLOC(zend_object_handlers);
-    memcpy(PROTOBUF_G(message_handlers), zend_get_std_object_handlers(),
-           sizeof(zend_object_handlers));
-    PROTOBUF_G(message_handlers)->write_property = message_set_property;
-    PROTOBUF_G(message_handlers)->read_property = message_get_property;
-  }
 
   registered_ce->create_object = message_create;
 }
+
+// -----------------------------------------------------------------------------
+// PHP Methods
+// -----------------------------------------------------------------------------
+
+PHP_METHOD(Message, readOneof) {
+  long index;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) ==
+      FAILURE) {
+    return;
+  }
+
+  MessageHeader* msg =
+      (MessageHeader*)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+  const upb_fielddef* field = upb_msgdef_itof(msg->descriptor->msgdef, index);
+
+  int property_cache_index =
+      msg->descriptor->layout->fields[upb_fielddef_index(field)].cache_index;
+  zval** cache_ptr = &(msg->std.properties_table)[property_cache_index];
+
+  layout_get(msg->descriptor->layout, message_data(msg), field,
+             return_value_ptr TSRMLS_CC);
+}
+
+PHP_METHOD(Message, writeOneof) {
+  long index;
+  zval* value;
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lz", &index, &value) ==
+      FAILURE) {
+    return;
+  }
+
+  MessageHeader* msg =
+      (MessageHeader*)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+  const upb_fielddef* field = upb_msgdef_itof(msg->descriptor->msgdef, index);
+
+  layout_set(msg->descriptor->layout, msg, field, value TSRMLS_CC);
+}
diff --git a/php/ext/google/protobuf/package.xml b/php/ext/google/protobuf/package.xml
new file mode 100644
index 0000000..8371909
--- /dev/null
+++ b/php/ext/google/protobuf/package.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<package packagerversion="1.9.5" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
+ <name>protobuf</name>
+ <channel>pecl.php.net</channel>
+ <summary>Google's language-neutral, platform-neutral, extensible mechanism for serializing structured data.</summary>
+ <description>https://developers.google.com/protocol-buffers/</description>
+ <lead>
+  <name>Bo Yang</name>
+  <user>teboring</user>
+  <email>protobuf-opensource@google.com</email>
+  <active>yes</active>
+ </lead>
+ <date>2016-09-02</date>
+ <time>16:06:07</time>
+ <version>
+  <release>3.1.0</release>
+  <api>3.1.0</api>
+ </version>
+ <stability>
+  <release>alpha</release>
+  <api>alpha</api>
+ </stability>
+ <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
+ <notes>
+First alpha release.
+ </notes>
+ <contents>
+  <dir baseinstalldir="/" name="/">
+    <file baseinstalldir="/" name="config.m4" role="src" />
+    <file baseinstalldir="/" name="array.c" role="src" />
+    <file baseinstalldir="/" name="def.c" role="src" />
+    <file baseinstalldir="/" name="encode_decode.c" role="src" />
+    <file baseinstalldir="/" name="map.c" role="src" />
+    <file baseinstalldir="/" name="message.c" role="src" />
+    <file baseinstalldir="/" name="protobuf.c" role="src" />
+    <file baseinstalldir="/" name="protobuf.h" role="src" />
+    <file baseinstalldir="/" name="storage.c" role="src" />
+    <file baseinstalldir="/" name="type_check.c" role="src" />
+    <file baseinstalldir="/" name="upb.c" role="src" />
+    <file baseinstalldir="/" name="upb.h" role="src" />
+    <file baseinstalldir="/" name="utf8.c" role="src" />
+    <file baseinstalldir="/" name="utf8.h" role="src" />
+  </dir>
+ </contents>
+ <dependencies>
+  <required>
+   <php>
+    <min>5.6.0</min>
+   </php>
+   <pearinstaller>
+    <min>1.4.0</min>
+   </pearinstaller>
+  </required>
+ </dependencies>
+ <providesextension>protobuf</providesextension>
+ <extsrcrelease />
+ <changelog>
+  <release>
+   <version>
+    <release>3.1.0</release>
+    <api>3.1.0</api>
+   </version>
+   <stability>
+    <release>alpha</release>
+    <api>alpha</api>
+   </stability>
+   <date>2016-09-02</date>
+   <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
+   <notes>
+First alpha release
+   </notes>
+  </release>
+ </changelog>
+</package>
diff --git a/php/ext/google/protobuf/protobuf.c b/php/ext/google/protobuf/protobuf.c
index b1ace8b..d8ad3c8 100644
--- a/php/ext/google/protobuf/protobuf.c
+++ b/php/ext/google/protobuf/protobuf.c
@@ -1,3 +1,33 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
 #include "protobuf.h"
 
 #include <zend_hash.h>
@@ -5,56 +35,81 @@
 ZEND_DECLARE_MODULE_GLOBALS(protobuf)
 static PHP_GINIT_FUNCTION(protobuf);
 static PHP_GSHUTDOWN_FUNCTION(protobuf);
+static PHP_RINIT_FUNCTION(protobuf);
+static PHP_RSHUTDOWN_FUNCTION(protobuf);
+static PHP_MINIT_FUNCTION(protobuf);
+static PHP_MSHUTDOWN_FUNCTION(protobuf);
 
-// -----------------------------------------------------------------------------
 // Global map from upb {msg,enum}defs to wrapper Descriptor/EnumDescriptor
 // instances.
+static HashTable* upb_def_to_php_obj_map;
+// Global map from message/enum's php class entry to corresponding wrapper
+// Descriptor/EnumDescriptor instances.
+static HashTable* ce_to_php_obj_map;
+
+// -----------------------------------------------------------------------------
+// Global maps.
 // -----------------------------------------------------------------------------
 
-void add_def_obj(const void* def, zval* value) {
-  uint nIndex = (ulong)def & PROTOBUF_G(upb_def_to_php_obj_map).nTableMask;
+static void add_to_table(HashTable* t, const void* def, void* value) {
+  uint nIndex = (ulong)def & t->nTableMask;
 
   zval* pDest = NULL;
-  Z_ADDREF_P(value);
-  zend_hash_index_update(&PROTOBUF_G(upb_def_to_php_obj_map), (zend_ulong)def,
-                         &value, sizeof(zval*), &pDest);
+  zend_hash_index_update(t, (zend_ulong)def, &value, sizeof(zval*), &pDest);
 }
 
-zval* get_def_obj(const void* def) {
-  zval** value;
-  if (zend_hash_index_find(&PROTOBUF_G(upb_def_to_php_obj_map), (zend_ulong)def,
-                           &value) == FAILURE) {
+static void* get_from_table(const HashTable* t, const void* def) {
+  void** value;
+  if (zend_hash_index_find(t, (zend_ulong)def, (void**)&value) == FAILURE) {
     zend_error(E_ERROR, "PHP object not found for given definition.\n");
     return NULL;
   }
   return *value;
 }
 
+static void add_to_list(HashTable* t, void* value) {
+  zval* pDest = NULL;
+  zend_hash_next_index_insert(t, &value, sizeof(void*), &pDest);
+}
+
+void add_def_obj(const void* def, zval* value) {
+  Z_ADDREF_P(value);
+  add_to_table(upb_def_to_php_obj_map, def, value);
+}
+
+zval* get_def_obj(const void* def) {
+  return (zval*)get_from_table(upb_def_to_php_obj_map, def);
+}
+
+void add_ce_obj(const void* ce, zval* value) {
+  Z_ADDREF_P(value);
+  add_to_table(ce_to_php_obj_map, ce, value);
+}
+
+zval* get_ce_obj(const void* ce) {
+  return (zval*)get_from_table(ce_to_php_obj_map, ce);
+}
+
 // -----------------------------------------------------------------------------
 // Utilities.
 // -----------------------------------------------------------------------------
 
-// define the function(s) we want to add
 zend_function_entry protobuf_functions[] = {
-  ZEND_FE(get_generated_pool, NULL)
   ZEND_FE_END
 };
 
-// "protobuf_functions" refers to the struct defined above
-// we'll be filling in more of this later: you can use this to specify
-// globals, php.ini info, startup and teardown functions, etc.
 zend_module_entry protobuf_module_entry = {
   STANDARD_MODULE_HEADER,
-  PHP_PROTOBUF_EXTNAME, // extension name
-  protobuf_functions,   // function list
-  PHP_MINIT(protobuf),  // process startup
-  NULL,  // process shutdown
-  NULL,  // request startup
-  NULL,  // request shutdown
-  NULL,  // extension info
+  PHP_PROTOBUF_EXTNAME,     // extension name
+  protobuf_functions,       // function list
+  PHP_MINIT(protobuf),      // process startup
+  PHP_MSHUTDOWN(protobuf),  // process shutdown
+  PHP_RINIT(protobuf),      // request shutdown
+  PHP_RSHUTDOWN(protobuf),  // request shutdown
+  NULL,                 // extension info
   PHP_PROTOBUF_VERSION, // extension version
   PHP_MODULE_GLOBALS(protobuf),  // globals descriptor
-  PHP_GINIT(protobuf), // globals ctor
+  PHP_GINIT(protobuf),  // globals ctor
   PHP_GSHUTDOWN(protobuf),  // globals dtor
   NULL,  // post deactivate
   STANDARD_MODULE_PROPERTIES_EX
@@ -65,25 +120,48 @@
 
 // global variables
 static PHP_GINIT_FUNCTION(protobuf) {
-  protobuf_globals->generated_pool = NULL;
-  generated_pool = NULL;
-  protobuf_globals->message_handlers = NULL;
-  zend_hash_init(&protobuf_globals->upb_def_to_php_obj_map, 16, NULL,
-                 ZVAL_PTR_DTOR, 0);
 }
 
 static PHP_GSHUTDOWN_FUNCTION(protobuf) {
-  if (protobuf_globals->generated_pool != NULL) {
-    FREE_ZVAL(protobuf_globals->generated_pool);
-  }
-  if (protobuf_globals->message_handlers != NULL) {
-    FREE(protobuf_globals->message_handlers);
-  }
-  zend_hash_destroy(&protobuf_globals->upb_def_to_php_obj_map);
 }
 
-PHP_MINIT_FUNCTION(protobuf) {
+static PHP_RINIT_FUNCTION(protobuf) {
+  ALLOC_HASHTABLE(upb_def_to_php_obj_map);
+  zend_hash_init(upb_def_to_php_obj_map, 16, NULL, ZVAL_PTR_DTOR, 0);
+
+  ALLOC_HASHTABLE(ce_to_php_obj_map);
+  zend_hash_init(ce_to_php_obj_map, 16, NULL, ZVAL_PTR_DTOR, 0);
+
+  generated_pool = NULL;
+  generated_pool_php = NULL;
+}
+
+static PHP_RSHUTDOWN_FUNCTION(protobuf) {
+  zend_hash_destroy(upb_def_to_php_obj_map);
+  FREE_HASHTABLE(upb_def_to_php_obj_map);
+
+  zend_hash_destroy(ce_to_php_obj_map);
+  FREE_HASHTABLE(ce_to_php_obj_map);
+
+  if (generated_pool_php != NULL) {
+    zval_dtor(generated_pool_php);
+    FREE_ZVAL(generated_pool_php);
+  }
+}
+
+static PHP_MINIT_FUNCTION(protobuf) {
+  map_field_init(TSRMLS_C);
+  repeated_field_init(TSRMLS_C);
+  gpb_type_init(TSRMLS_C);
+  message_init(TSRMLS_C);
   descriptor_pool_init(TSRMLS_C);
   descriptor_init(TSRMLS_C);
-  message_builder_context_init(TSRMLS_C);
+  enum_descriptor_init(TSRMLS_C);
+  util_init(TSRMLS_C);
+}
+
+static PHP_MSHUTDOWN_FUNCTION(protobuf) {
+  PEFREE(message_handlers);
+  PEFREE(repeated_field_handlers);
+  PEFREE(map_field_handlers);
 }
diff --git a/php/ext/google/protobuf/protobuf.h b/php/ext/google/protobuf/protobuf.h
index f903855..0330f36 100644
--- a/php/ext/google/protobuf/protobuf.h
+++ b/php/ext/google/protobuf/protobuf.h
@@ -1,46 +1,73 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
 #ifndef __GOOGLE_PROTOBUF_PHP_PROTOBUF_H__
 #define __GOOGLE_PROTOBUF_PHP_PROTOBUF_H__
 
 #include <php.h>
 
+// ubp.h has to be placed after php.h. Othwise, php.h will introduce NDEBUG.
 #include "upb.h"
 
 #define PHP_PROTOBUF_EXTNAME "protobuf"
 #define PHP_PROTOBUF_VERSION "0.01"
 
-// Forward decls.
+// -----------------------------------------------------------------------------
+// Forward Declaration
+// ----------------------------------------------------------------------------
+
 struct DescriptorPool;
 struct Descriptor;
-struct FieldDescriptor;
 struct EnumDescriptor;
-struct MessageLayout;
+struct FieldDescriptor;
 struct MessageField;
 struct MessageHeader;
-struct MessageBuilderContext;
-struct EnumBuilderContext;
+struct MessageLayout;
+struct RepeatedField;
+struct MapField;
 
 typedef struct DescriptorPool DescriptorPool;
 typedef struct Descriptor Descriptor;
-typedef struct FieldDescriptor FieldDescriptor;
-typedef struct OneofDescriptor OneofDescriptor;
 typedef struct EnumDescriptor EnumDescriptor;
-typedef struct MessageLayout MessageLayout;
+typedef struct FieldDescriptor FieldDescriptor;
 typedef struct MessageField MessageField;
 typedef struct MessageHeader MessageHeader;
-typedef struct MessageBuilderContext MessageBuilderContext;
-typedef struct OneofBuilderContext OneofBuilderContext;
-typedef struct EnumBuilderContext EnumBuilderContext;
+typedef struct MessageLayout MessageLayout;
+typedef struct RepeatedField RepeatedField;
+typedef struct MapField MapField;
 
-extern zend_class_entry* builder_type;
-extern zend_class_entry* descriptor_type;
-extern zend_class_entry* message_builder_context_type;
-
-extern DescriptorPool* generated_pool;  // The actual generated pool
+// -----------------------------------------------------------------------------
+// Globals.
+// -----------------------------------------------------------------------------
 
 ZEND_BEGIN_MODULE_GLOBALS(protobuf)
-  zval* generated_pool;
-  zend_object_handlers* message_handlers;
-  HashTable upb_def_to_php_obj_map;
 ZEND_END_MODULE_GLOBALS(protobuf)
 
 ZEND_DECLARE_MODULE_GLOBALS(protobuf)
@@ -51,14 +78,31 @@
 #define PROTOBUF_G(v) (protobuf_globals.v)
 #endif
 
-// -----------------------------------------------------------------------------
-// PHP functions and global variables.
-// -----------------------------------------------------------------------------
+// Init module and PHP classes.
+void descriptor_init(TSRMLS_D);
+void enum_descriptor_init(TSRMLS_D);
+void descriptor_pool_init(TSRMLS_D);
+void gpb_type_init(TSRMLS_D);
+void map_field_init(TSRMLS_D);
+void repeated_field_init(TSRMLS_D);
+void util_init(TSRMLS_D);
+void message_init(TSRMLS_D);
 
-PHP_MINIT_FUNCTION(protobuf);
+// Global map from upb {msg,enum}defs to wrapper Descriptor/EnumDescriptor
+// instances.
+void add_def_obj(const void* def, zval* value);
+zval* get_def_obj(const void* def);
+
+// Global map from PHP class entries to wrapper Descriptor/EnumDescriptor
+// instances.
+void add_ce_obj(const void* ce, zval* value);
+zval* get_ce_obj(const void* ce);
+
+extern zend_class_entry* map_field_type;
+extern zend_class_entry* repeated_field_type;
 
 // -----------------------------------------------------------------------------
-// PHP class structure.
+// Descriptor.
 // -----------------------------------------------------------------------------
 
 struct DescriptorPool {
@@ -67,72 +111,112 @@
   HashTable* pending_list;
 };
 
+PHP_METHOD(DescriptorPool, getGeneratedPool);
+PHP_METHOD(DescriptorPool, internalAddGeneratedFile);
+
+extern zval* generated_pool_php;  // wrapper of generated pool
+extern DescriptorPool* generated_pool;  // The actual generated pool
+
 struct Descriptor {
   zend_object std;
   const upb_msgdef* msgdef;
   MessageLayout* layout;
-  // zval* klass;  // begins as NULL
-  // const upb_handlers* fill_handlers;
-  // const upb_pbdecodermethod* fill_method;
+  zend_class_entry* klass;  // begins as NULL
+  const upb_handlers* fill_handlers;
+  const upb_pbdecodermethod* fill_method;
   const upb_handlers* pb_serialize_handlers;
-  // const upb_handlers* json_serialize_handlers;
-  // Handlers hold type class references for sub-message fields directly in some
-  // cases. We need to keep these rooted because they might otherwise be
-  // collected.
-  // zval_array typeclass_references;
 };
 
+extern zend_class_entry* descriptor_type;
+
+void descriptor_name_set(Descriptor *desc, const char *name);
+
 struct FieldDescriptor {
   zend_object std;
   const upb_fielddef* fielddef;
 };
 
-struct OneofDescriptor {
-  zend_object std;
-  const upb_oneofdef* oneofdef;
-};
-
 struct EnumDescriptor {
   zend_object std;
   const upb_enumdef* enumdef;
-  // zval* module;  // begins as NULL
+  zend_class_entry* klass;  // begins as NULL
+  // VALUE module;  // begins as nil
 };
 
+extern zend_class_entry* enum_descriptor_type;
+
 // -----------------------------------------------------------------------------
-// Native slot storage abstraction.
+// Message class creation.
 // -----------------------------------------------------------------------------
 
-#define NATIVE_SLOT_MAX_SIZE sizeof(uint64_t)
+void* message_data(void* msg);
 
-size_t native_slot_size(upb_fieldtype_t type);
+// Build PHP class for given descriptor. Instead of building from scratch, this
+// function modifies existing class which has been partially defined in PHP
+// code.
+void build_class_from_descriptor(zval* php_descriptor TSRMLS_DC);
 
-#define MAP_KEY_FIELD 1
-#define MAP_VALUE_FIELD 2
-
-// Oneof case slot value to indicate that no oneof case is set. The value `0` is
-// safe because field numbers are used as case identifiers, and no field can
-// have a number of 0.
-#define ONEOF_CASE_NONE 0
-
-// These operate on a map field (i.e., a repeated field of submessages whose
-// submessage type is a map-entry msgdef).
-bool is_map_field(const upb_fielddef* field);
-const upb_fielddef* map_field_key(const upb_fielddef* field);
-const upb_fielddef* map_field_value(const upb_fielddef* field);
-
-// These operate on a map-entry msgdef.
-const upb_fielddef* map_entry_key(const upb_msgdef* msgdef);
-const upb_fielddef* map_entry_value(const upb_msgdef* msgdef);
+extern zend_object_handlers* message_handlers;
 
 // -----------------------------------------------------------------------------
 // Message layout / storage.
 // -----------------------------------------------------------------------------
 
+/*
+ * In c extension, each protobuf message is a zval instance. The zval instance
+ * is like union, which can be used to store int, string, zend_object_value and
+ * etc. For protobuf message, the zval instance is used to store the
+ * zend_object_value.
+ *
+ * The zend_object_value is composed of handlers and a handle to look up the
+ * actual stored data. The handlers are pointers to functions, e.g., read,
+ * write, and etc, to access properties.
+ *
+ * The actual data of protobuf messages is stored as MessageHeader in zend
+ * engine's central repository. Each MessageHeader instance is composed of a
+ * zend_object, a Descriptor instance and the real message data.
+ *
+ * For the reason that PHP's native types may not be large enough to store
+ * protobuf message's field (e.g., int64), all message's data is stored in
+ * custom memory layout and is indexed by the Descriptor instance.
+ *
+ * The zend_object contains the zend class entry and the properties table. The
+ * zend class entry contains all information about protobuf message's
+ * corresponding PHP class. The most useful information is the offset table of
+ * properties. Because read access to properties requires returning zval
+ * instance, we need to convert data from the custom layout to zval instance.
+ * Instead of creating zval instance for every read access, we use the zval
+ * instances in the properties table in the zend_object as cache.  When
+ * accessing properties, the offset is needed to find the zval property in
+ * zend_object's properties table. These properties will be updated using the
+ * data from custom memory layout only when reading these properties.
+ *
+ * zval
+ * |-zend_object_value obj
+ *   |-zend_object_handlers* handlers -> |-read_property_handler
+ *   |                                   |-write_property_handler
+ *   |                              ++++++++++++++++++++++
+ *   |-zend_object_handle handle -> + central repository +
+ *                                  ++++++++++++++++++++++
+ *  MessageHeader <-----------------|
+ *  |-zend_object std
+ *  | |-class_entry* ce -> class_entry
+ *  | |                    |-HashTable properties_table (name->offset)
+ *  | |-zval** properties_table <------------------------------|
+ *  |                         |------> zval* property(cache)
+ *  |-Descriptor* desc (name->offset)
+ *  |-void** data <-----------|
+ *           |-----------------------> void* property(data)
+ *
+ */
+
 #define MESSAGE_FIELD_NO_CASE ((size_t)-1)
 
 struct MessageField {
   size_t offset;
-  size_t case_offset;  // for oneofs, a uint32. Else, MESSAGE_FIELD_NO_CASE.
+  int cache_index;  // Each field except oneof field has a zval cache to avoid
+                    // multiple creation when being accessed.
+  size_t case_offset;   // for oneofs, a uint32. Else, MESSAGE_FIELD_NO_CASE.
 };
 
 struct MessageLayout {
@@ -141,141 +225,230 @@
   size_t size;
 };
 
-void layout_init(MessageLayout* layout, void* storage);
-zval* layout_get(MessageLayout* layout, const void* storage,
-                 const upb_fielddef* field TSRMLS_DC);
-MessageLayout* create_layout(const upb_msgdef* msgdef);
-void free_layout(MessageLayout* layout);
-zval* native_slot_get(upb_fieldtype_t type, /*VALUE type_class,*/
-                      const void* memory TSRMLS_DC);
-
-// -----------------------------------------------------------------------------
-// Message class creation.
-// -----------------------------------------------------------------------------
-
 struct MessageHeader {
-  zend_object std;
-  Descriptor* descriptor;  // kept alive by self.class.descriptor reference.
-                           // Data comes after this.
+  zend_object std;  // Stores properties table and class info of PHP instance.
+                    // This is needed for MessageHeader to be accessed via PHP.
+  Descriptor* descriptor;  // Kept alive by self.class.descriptor reference.
+  // The real message data is appended after MessageHeader.
 };
 
-struct MessageBuilderContext {
-  zend_object std;
-  zval* descriptor;
-  zval* pool;
-};
+MessageLayout* create_layout(const upb_msgdef* msgdef);
+void layout_init(MessageLayout* layout, void* storage, zval** properties_table);
+zval* layout_get(MessageLayout* layout, const void* storage,
+                 const upb_fielddef* field, zval** cache TSRMLS_DC);
+void layout_set(MessageLayout* layout, MessageHeader* header,
+                const upb_fielddef* field, zval* val);
+void free_layout(MessageLayout* layout);
 
-struct OneofBuilderContext {
-  zend_object std;
-  // VALUE descriptor;
-  // VALUE builder;
-};
+PHP_METHOD(Message, readOneof);
+PHP_METHOD(Message, writeOneof);
 
-struct EnumBuilderContext {
-  zend_object std;
-  // VALUE enumdesc;
-};
+// -----------------------------------------------------------------------------
+// Encode / Decode.
+// -----------------------------------------------------------------------------
 
-// Forward-declare all of the PHP method implementations.
+// Maximum depth allowed during encoding, to avoid stack overflows due to
+// cycles.
+#define ENCODE_MAX_NESTING 63
 
-DescriptorPool* php_to_descriptor_pool(zval* value TSRMLS_DC);
-zend_object_value descriptor_pool_create(zend_class_entry *ce TSRMLS_DC);
-void descriptor_pool_free_c(DescriptorPool* object TSRMLS_DC);
-void descriptor_pool_free(void* object TSRMLS_DC);
-void descriptor_pool_init_c_instance(DescriptorPool* pool TSRMLS_DC);
-PHP_METHOD(DescriptorPool, addMessage);
-PHP_METHOD(DescriptorPool, finalize);
-
-Descriptor* php_to_descriptor(zval* value TSRMLS_DC);
-zend_object_value descriptor_create(zend_class_entry *ce TSRMLS_DC);
-void descriptor_init_c_instance(Descriptor* intern TSRMLS_DC);
-void descriptor_free_c(Descriptor* object TSRMLS_DC);
-void descriptor_free(void* object TSRMLS_DC);
-void descriptor_name_set(Descriptor *desc, const char *name);
-
-MessageBuilderContext* php_to_message_builder_context(zval* value TSRMLS_DC);
-zend_object_value message_builder_context_create(
-    zend_class_entry* ce TSRMLS_DC);
-void message_builder_context_init_c_instance(
-    MessageBuilderContext* intern TSRMLS_DC);
-void message_builder_context_free_c(MessageBuilderContext* object TSRMLS_DC);
-void message_builder_context_free(void* object TSRMLS_DC);
-PHP_METHOD(MessageBuilderContext, optional);
-PHP_METHOD(MessageBuilderContext, finalizeToPool);
+// Constructs the upb decoder method for parsing messages of this type.
+// This is called from the message class creation code.
+const upb_pbdecodermethod *new_fillmsg_decodermethod(Descriptor *desc,
+                                                     const void *owner);
 
 PHP_METHOD(Message, encode);
-const zend_class_entry* build_class_from_descriptor(
-    zval* php_descriptor TSRMLS_DC);
-
-PHP_FUNCTION(get_generated_pool);
+PHP_METHOD(Message, decode);
 
 // -----------------------------------------------------------------------------
-// Global map from upb {msg,enum}defs to wrapper Descriptor/EnumDescriptor
-// instances.
-// ----------------------------------------------------------------------------
+// Type check / conversion.
+// -----------------------------------------------------------------------------
 
-void add_def_obj(const void* def, zval* value);
-zval* get_def_obj(const void* def);
+bool protobuf_convert_to_int32(zval* from, int32_t* to);
+bool protobuf_convert_to_uint32(zval* from, uint32_t* to);
+bool protobuf_convert_to_int64(zval* from, int64_t* to);
+bool protobuf_convert_to_uint64(zval* from, uint64_t* to);
+bool protobuf_convert_to_float(zval* from, float* to);
+bool protobuf_convert_to_double(zval* from, double* to);
+bool protobuf_convert_to_bool(zval* from, int8_t* to);
+bool protobuf_convert_to_string(zval* from);
+
+PHP_METHOD(Util, checkInt32);
+PHP_METHOD(Util, checkUint32);
+PHP_METHOD(Util, checkInt64);
+PHP_METHOD(Util, checkUint64);
+PHP_METHOD(Util, checkEnum);
+PHP_METHOD(Util, checkFloat);
+PHP_METHOD(Util, checkDouble);
+PHP_METHOD(Util, checkBool);
+PHP_METHOD(Util, checkString);
+PHP_METHOD(Util, checkBytes);
+PHP_METHOD(Util, checkMessage);
+PHP_METHOD(Util, checkRepeatedField);
+
+// -----------------------------------------------------------------------------
+// Native slot storage abstraction.
+// -----------------------------------------------------------------------------
+
+#define NATIVE_SLOT_MAX_SIZE sizeof(uint64_t)
+
+size_t native_slot_size(upb_fieldtype_t type);
+bool native_slot_set(upb_fieldtype_t type, const zend_class_entry* klass,
+                     void* memory, zval* value);
+void native_slot_init(upb_fieldtype_t type, void* memory, zval** cache);
+// For each property, in order to avoid conversion between the zval object and
+// the actual data type during parsing/serialization, the containing message
+// object use the custom memory layout to store the actual data type for each
+// property inside of it.  To access a property from php code, the property
+// needs to be converted to a zval object. The message object is not responsible
+// for providing such a zval object. Instead the caller needs to provide one
+// (cache) and update it with the actual data (memory).
+void native_slot_get(upb_fieldtype_t type, const void* memory,
+                     zval** cache TSRMLS_DC);
+void native_slot_get_default(upb_fieldtype_t type, zval** cache TSRMLS_DC);
+
+// -----------------------------------------------------------------------------
+// Map Field.
+// -----------------------------------------------------------------------------
+
+extern zend_object_handlers* map_field_handlers;
+
+typedef struct {
+  zend_object std;
+  upb_fieldtype_t key_type;
+  upb_fieldtype_t value_type;
+  const zend_class_entry* msg_ce;  // class entry for value message
+  upb_strtable table;
+} Map;
+
+typedef struct {
+  Map* self;
+  upb_strtable_iter it;
+} MapIter;
+
+void map_begin(zval* self, MapIter* iter);
+void map_next(MapIter* iter);
+bool map_done(MapIter* iter);
+const char* map_iter_key(MapIter* iter, int* len);
+upb_value map_iter_value(MapIter* iter, int* len);
+
+// These operate on a map-entry msgdef.
+const upb_fielddef* map_entry_key(const upb_msgdef* msgdef);
+const upb_fielddef* map_entry_value(const upb_msgdef* msgdef);
+
+zend_object_value map_field_create(zend_class_entry *ce TSRMLS_DC);
+void map_field_create_with_type(zend_class_entry *ce, const upb_fielddef *field,
+                                zval **map_field TSRMLS_DC);
+void map_field_free(void* object TSRMLS_DC);
+void* upb_value_memory(upb_value* v);
+
+#define MAP_KEY_FIELD 1
+#define MAP_VALUE_FIELD 2
+
+// These operate on a map field (i.e., a repeated field of submessages whose
+// submessage type is a map-entry msgdef).
+const upb_fielddef* map_field_key(const upb_fielddef* field);
+const upb_fielddef* map_field_value(const upb_fielddef* field);
+
+bool map_index_set(Map *intern, const char* keyval, int length, upb_value v);
+
+PHP_METHOD(MapField, __construct);
+PHP_METHOD(MapField, offsetExists);
+PHP_METHOD(MapField, offsetGet);
+PHP_METHOD(MapField, offsetSet);
+PHP_METHOD(MapField, offsetUnset);
+PHP_METHOD(MapField, count);
+
+// -----------------------------------------------------------------------------
+// Repeated Field.
+// -----------------------------------------------------------------------------
+
+extern zend_object_handlers* repeated_field_handlers;
+
+struct RepeatedField {
+  zend_object std;
+  zval* array;
+  upb_fieldtype_t type;
+  const zend_class_entry* msg_ce;  // class entry for containing message
+                                   // (for message field only).
+};
+
+void repeated_field_create_with_type(zend_class_entry* ce,
+                                     const upb_fielddef* field,
+                                     zval** repeated_field TSRMLS_DC);
+// Return the element at the index position from the repeated field. There is
+// not restriction on the type of stored elements.
+void *repeated_field_index_native(RepeatedField *intern, int index);
+// Add the element to the end of the repeated field. There is not restriction on
+// the type of stored elements.
+void repeated_field_push_native(RepeatedField *intern, void *value);
+
+PHP_METHOD(RepeatedField, __construct);
+PHP_METHOD(RepeatedField, append);
+PHP_METHOD(RepeatedField, offsetExists);
+PHP_METHOD(RepeatedField, offsetGet);
+PHP_METHOD(RepeatedField, offsetSet);
+PHP_METHOD(RepeatedField, offsetUnset);
+PHP_METHOD(RepeatedField, count);
+
+// -----------------------------------------------------------------------------
+// Oneof Field.
+// -----------------------------------------------------------------------------
+
+typedef struct {
+  zend_object std;
+  upb_oneofdef* oneofdef;
+  int index;    // Index of field in oneof. -1 if not set.
+  char value[NATIVE_SLOT_MAX_SIZE];
+} Oneof;
+
+// Oneof case slot value to indicate that no oneof case is set. The value `0` is
+// safe because field numbers are used as case identifiers, and no field can
+// have a number of 0.
+#define ONEOF_CASE_NONE 0
+
+// -----------------------------------------------------------------------------
+// Upb.
+// -----------------------------------------------------------------------------
+
+upb_fieldtype_t to_fieldtype(upb_descriptortype_t type);
+const zend_class_entry *field_type_class(const upb_fielddef *field);
 
 // -----------------------------------------------------------------------------
 // Utilities.
 // -----------------------------------------------------------------------------
 
-// PHP Array utils.
-#define Z_ARRVAL_SIZE_P(zval_p) zend_hash_num_elements(Z_ARRVAL_P(zval_p))
-#define Z_ARRVAL_BEGIN_P(zval_p) Z_ARRVAL_P(zval_p)->pListHead
-#define Z_BUCKET_NEXT_PP(bucket_pp) *bucket_pp = (*bucket_pp)->pListNext
+// PHP <-> C conversion.
+#define UNBOX(class_name, val) \
+  (class_name*)zend_object_store_get_object(val TSRMLS_CC);
 
-#define DEFINE_PHP_OBJECT(class_name, class_name_lower, name) \
-  do {                                                        \
-    zval* name;                                               \
-    MAKE_STD_ZVAL(name);                                      \
-    object_init_ex(name, class_name_lower##_type);            \
-  } while (0)
-
-#define DEFINE_PHP_WRAPPER(class_name, class_name_lower, name, intern) \
-  zval* name;                                                          \
-  MAKE_STD_ZVAL(name);                                                 \
-  object_init_ex(name, class_name_lower##_type);                       \
-  Z_OBJVAL_P(name)                                                     \
-      .handle = zend_objects_store_put(                                \
-      intern, (zend_objects_store_dtor_t)zend_objects_destroy_object,  \
-      class_name_lower##_free, NULL TSRMLS_CC);
-
-#define DEFINE_PHP_ZVAL(name) \
-  do {                        \
-    zval* name;               \
-    MAKE_STD_ZVAL(name);      \
-  } while (0)
-
-#define DEFINE_PHP_STRING(name, value) \
-  do {                                 \
-    zval* name;                        \
-    MAKE_STD_ZVAL(name);               \
-    ZVAL_STRING(name, value, 1);       \
-  } while (0)
-
-// Upb Utilities
-
-void check_upb_status(const upb_status* status, const char* msg);
-
-#define CHECK_UPB(code, msg)             \
-  do {                                   \
-    upb_status status = UPB_STATUS_INIT; \
-    code;                                \
-    check_upb_status(&status, msg);      \
-  } while (0)
+#define BOX(class_name, wrapper, intern, free_func)                    \
+  MAKE_STD_ZVAL(wrapper);                                              \
+  Z_TYPE_P(wrapper) = IS_OBJECT;                                       \
+  Z_OBJVAL_P(wrapper)                                                  \
+      .handle =                                                        \
+      zend_objects_store_put(intern, NULL, free_func, NULL TSRMLS_CC); \
+  Z_OBJVAL_P(wrapper).handlers = zend_get_std_object_handlers();
 
 // Memory management
-
 #define ALLOC(class_name) (class_name*) emalloc(sizeof(class_name))
+#define PEMALLOC(class_name) (class_name*) pemalloc(sizeof(class_name), 1)
 #define ALLOC_N(class_name, n) (class_name*) emalloc(sizeof(class_name) * n)
 #define FREE(object) efree(object)
+#define PEFREE(object) pefree(object, 1)
 
-// Type Checking
-#define CHECK_TYPE(field, type)             \
-  if (Z_TYPE_P(field) != type) {            \
-    zend_error(E_ERROR, "Unexpected type"); \
-  }
+// Create PHP internal instance.
+#define CREATE(class_name, intern, init_func) \
+  intern = ALLOC(class_name);                 \
+  memset(intern, 0, sizeof(class_name));      \
+  init_func(intern TSRMLS_CC);
+
+// String argument.
+#define STR(str) (str), strlen(str)
+
+// Zend Value
+#define Z_OBJ_P(zval_p)                                       \
+  ((zend_object*)(EG(objects_store)                           \
+                      .object_buckets[Z_OBJ_HANDLE_P(zval_p)] \
+                      .bucket.obj.object))
 
 #endif  // __GOOGLE_PROTOBUF_PHP_PROTOBUF_H__
diff --git a/php/ext/google/protobuf/storage.c b/php/ext/google/protobuf/storage.c
index e5a09c1..7423552 100644
--- a/php/ext/google/protobuf/storage.c
+++ b/php/ext/google/protobuf/storage.c
@@ -1,19 +1,41 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
 #include <stdint.h>
 #include <protobuf.h>
+#include <Zend/zend.h>
 
 // -----------------------------------------------------------------------------
-// PHP <-> native slot management.
+// Native slot storage.
 // -----------------------------------------------------------------------------
 
-static zval* int32_to_zval(int32_t value) {
-  zval* tmp;
-  MAKE_STD_ZVAL(tmp);
-  ZVAL_LONG(tmp, value);
-  php_printf("int32 to zval\n");
-  // ZVAL_LONG(tmp, 1);
-  return tmp;
-}
-
 #define DEREF(memory, type) *(type*)(memory)
 
 size_t native_slot_size(upb_fieldtype_t type) {
@@ -21,9 +43,9 @@
     case UPB_TYPE_FLOAT: return 4;
     case UPB_TYPE_DOUBLE: return 8;
     case UPB_TYPE_BOOL: return 1;
-    case UPB_TYPE_STRING: return sizeof(zval*);
-    case UPB_TYPE_BYTES: return sizeof(zval*);
-    case UPB_TYPE_MESSAGE: return sizeof(zval*);
+    case UPB_TYPE_STRING: return sizeof(void*);
+    case UPB_TYPE_BYTES: return sizeof(void*);
+    case UPB_TYPE_MESSAGE: return sizeof(void*);
     case UPB_TYPE_ENUM: return 4;
     case UPB_TYPE_INT32: return 4;
     case UPB_TYPE_INT64: return 8;
@@ -33,72 +55,77 @@
   }
 }
 
-static bool is_php_num(zval* value) {
-  // Is numerial string also valid?
-  return (Z_TYPE_P(value) == IS_LONG ||
-          Z_TYPE_P(value) == IS_DOUBLE);
-}
-
-void native_slot_check_int_range_precision(upb_fieldtype_t type, zval* val) {
-  // TODO(teboring): Add it back.
-  // if (!is_php_num(val)) {
-  //   zend_error(E_ERROR, "Expected number type for integral field.");
-  // }
-
-  // if (Z_TYPE_P(val) == IS_DOUBLE) {
-  //   double dbl_val = NUM2DBL(val);
-  //   if (floor(dbl_val) != dbl_val) {
-  //     zend_error(E_ERROR,
-  //              "Non-integral floating point value assigned to integer field.");
-  //   }
-  // }
-  // if (type == UPB_TYPE_UINT32 || type == UPB_TYPE_UINT64) {
-  //   if (NUM2DBL(val) < 0) {
-  //     zend_error(E_ERROR,
-  //              "Assigning negative value to unsigned integer field.");
-  //   }
-  // }
-}
-
-zval* native_slot_get(upb_fieldtype_t type, /*VALUE type_class,*/
-                      const void* memory TSRMLS_DC) {
-  zval* retval = NULL;
+bool native_slot_set(upb_fieldtype_t type, const zend_class_entry* klass,
+                     void* memory, zval* value) {
   switch (type) {
-    // TODO(teboring): Add it back.
-    // case UPB_TYPE_FLOAT:
-    //   return DBL2NUM(DEREF(memory, float));
-    // case UPB_TYPE_DOUBLE:
-    //   return DBL2NUM(DEREF(memory, double));
-    // case UPB_TYPE_BOOL:
-    //   return DEREF(memory, int8_t) ? Qtrue : Qfalse;
-    // case UPB_TYPE_STRING:
-    // case UPB_TYPE_BYTES:
-    // case UPB_TYPE_MESSAGE:
-    //   return DEREF(memory, VALUE);
-    // case UPB_TYPE_ENUM: {
-    //   int32_t val = DEREF(memory, int32_t);
-    //   VALUE symbol = enum_lookup(type_class, INT2NUM(val));
-    //   if (symbol == Qnil) {
-    //     return INT2NUM(val);
-    //   } else {
-    //     return symbol;
-    //   }
-    // }
-    case UPB_TYPE_INT32:
-      return int32_to_zval(DEREF(memory, int32_t));
-    // TODO(teboring): Add it back.
-    // case UPB_TYPE_INT64:
-    //   return LL2NUM(DEREF(memory, int64_t));
-    // case UPB_TYPE_UINT32:
-    //   return UINT2NUM(DEREF(memory, uint32_t));
-    // case UPB_TYPE_UINT64:
-    //   return ULL2NUM(DEREF(memory, uint64_t));
-    default:
-      return EG(uninitialized_zval_ptr);
+    case UPB_TYPE_STRING:
+    case UPB_TYPE_BYTES: {
+      if (!protobuf_convert_to_string(value)) {
+        return false;
+      }
+      if (type == UPB_TYPE_STRING &&
+          !is_structurally_valid_utf8(Z_STRVAL_P(value), Z_STRLEN_P(value))) {
+        zend_error(E_USER_ERROR, "Given string is not UTF8 encoded.");
+        return false;
+      }
+      if (*(zval**)memory != NULL) {
+        REPLACE_ZVAL_VALUE((zval**)memory, value, 1);
+      } else {
+        // Handles repeated/map string field. Memory provided by
+        // RepeatedField/Map is not initialized.
+        MAKE_STD_ZVAL(DEREF(memory, zval*));
+        ZVAL_STRINGL(DEREF(memory, zval*), Z_STRVAL_P(value), Z_STRLEN_P(value),
+                     1);
+      }
+      break;
+    }
+    case UPB_TYPE_MESSAGE: {
+      if (Z_TYPE_P(value) != IS_OBJECT && Z_TYPE_P(value) != IS_NULL) {
+        zend_error(E_USER_ERROR, "Given value is not message.");
+        return false;
+      }
+      if (Z_TYPE_P(value) == IS_OBJECT && klass != Z_OBJCE_P(value)) {
+        zend_error(E_USER_ERROR, "Given message does not have correct class.");
+        return false;
+      }
+      if (EXPECTED(DEREF(memory, zval*) != value)) {
+        if (DEREF(memory, zval*) != NULL) {
+          zval_ptr_dtor((zval**)memory);
+        }
+        DEREF(memory, zval*) = value;
+        Z_ADDREF_P(value);
+      }
+      break;
+    }
+
+#define CASE_TYPE(upb_type, type, c_type, php_type)              \
+  case UPB_TYPE_##upb_type: {                                    \
+    c_type type##_value;                                         \
+    if (protobuf_convert_to_##type(value, &type##_value)) {      \
+      DEREF(memory, c_type) = type##_value;                      \
+    }                                                            \
+    break;                                                       \
   }
+      CASE_TYPE(INT32,  int32,  int32_t,  LONG)
+      CASE_TYPE(UINT32, uint32, uint32_t, LONG)
+      CASE_TYPE(ENUM,   int32,  int32_t,  LONG)
+      CASE_TYPE(INT64,  int64,  int64_t,  LONG)
+      CASE_TYPE(UINT64, uint64, uint64_t, LONG)
+      CASE_TYPE(FLOAT,  float,  float,    DOUBLE)
+      CASE_TYPE(DOUBLE, double, double,   DOUBLE)
+      CASE_TYPE(BOOL,   bool,   int8_t,   BOOL)
+
+#undef CASE_TYPE
+
+    default:
+      break;
+  }
+
+  return true;
 }
 
-void native_slot_init(upb_fieldtype_t type, void* memory) {
+void native_slot_init(upb_fieldtype_t type, void* memory, zval** cache) {
+  zval* tmp = NULL;
   switch (type) {
     case UPB_TYPE_FLOAT:
       DEREF(memory, float) = 0.0;
@@ -109,17 +136,11 @@
     case UPB_TYPE_BOOL:
       DEREF(memory, int8_t) = 0;
       break;
-    // TODO(teboring): Add it back.
-    // case UPB_TYPE_STRING:
-    // case UPB_TYPE_BYTES:
-    //   DEREF(memory, VALUE) = php_str_new2("");
-    //   php_enc_associate(DEREF(memory, VALUE), (type == UPB_TYPE_BYTES)
-    //                                              ? kRubyString8bitEncoding
-    //                                              : kRubyStringUtf8Encoding);
-    //   break;
-    // case UPB_TYPE_MESSAGE:
-    //   DEREF(memory, VALUE) = Qnil;
-    //   break;
+    case UPB_TYPE_STRING:
+    case UPB_TYPE_BYTES:
+    case UPB_TYPE_MESSAGE:
+      DEREF(memory, zval**) = cache;
+      break;
     case UPB_TYPE_ENUM:
     case UPB_TYPE_INT32:
       DEREF(memory, int32_t) = 0;
@@ -138,122 +159,93 @@
   }
 }
 
-void native_slot_set(upb_fieldtype_t type, /*VALUE type_class,*/ void* memory,
-                     zval* value) {
-  native_slot_set_value_and_case(type, /*type_class,*/ memory, value, NULL, 0);
-}
-
-void native_slot_set_value_and_case(upb_fieldtype_t type, /*VALUE type_class,*/
-                                    void* memory, zval* value,
-                                    uint32_t* case_memory,
-                                    uint32_t case_number) {
+void native_slot_get(upb_fieldtype_t type, const void* memory,
+                     zval** cache TSRMLS_DC) {
   switch (type) {
-    case UPB_TYPE_FLOAT:
-      if (!Z_TYPE_P(value) == IS_LONG) {
-        zend_error(E_ERROR, "Expected number type for float field.");
+#define CASE(upb_type, php_type, c_type) \
+    case UPB_TYPE_##upb_type: \
+      SEPARATE_ZVAL_IF_NOT_REF(cache); \
+      ZVAL_##php_type(*cache, DEREF(memory, c_type)); \
+      return;
+
+CASE(FLOAT,  DOUBLE, float)
+CASE(DOUBLE, DOUBLE, double)
+CASE(BOOL,   BOOL,   int8_t)
+CASE(INT32,  LONG,   int32_t)
+CASE(INT64,  LONG,   int64_t)
+CASE(UINT64, LONG,   uint64_t)
+CASE(ENUM,   LONG,   uint32_t)
+
+#undef CASE
+    case UPB_TYPE_UINT32: {
+      // Prepend bit-1 for negative numbers, so that uint32 value will be
+      // consistent on both 32-bit and 64-bit architectures.
+      SEPARATE_ZVAL_IF_NOT_REF(cache);
+      int value = DEREF(memory, int32_t);
+      if (sizeof(int) == 8) {
+        value |= (-((value >> 31) & 0x1) & 0xFFFFFFFF00000000);
       }
-      DEREF(memory, float) = Z_DVAL_P(value);
-      break;
-    case UPB_TYPE_DOUBLE:
-      // TODO(teboring): Add it back.
-      // if (!is_php_num(value)) {
-      //   zend_error(E_ERROR, "Expected number type for double field.");
-      // }
-      // DEREF(memory, double) = Z_DVAL_P(value);
-      break;
-    case UPB_TYPE_BOOL: {
-      int8_t val = -1;
-      if (zval_is_true(value)) {
-        val = 1;
-      } else {
-        val = 0;
-      }
-      // TODO(teboring): Add it back.
-      // else if (value == Qfalse) {
-      //   val = 0;
-      // }
-      // else {
-      //   php_raise(php_eTypeError, "Invalid argument for boolean field.");
-      // }
-      DEREF(memory, int8_t) = val;
-      break;
+      ZVAL_LONG(*cache, value);
+      return;
     }
+
     case UPB_TYPE_STRING:
     case UPB_TYPE_BYTES: {
-      // TODO(teboring): Add it back.
-      // if (Z_TYPE_P(value) != IS_STRING) {
-      //   zend_error(E_ERROR, "Invalid argument for string field.");
-      // }
-      // native_slot_validate_string_encoding(type, value);
-      // DEREF(memory, zval*) = value;
+      // For optional string/bytes fields, the cache is owned by the containing
+      // message and should have been updated during setting/decoding. However,
+      // for repeated string/bytes fields, the cache is provided by zend engine
+      // and has not been updated.
+      zval* value = DEREF(memory, zval*);
+      if (*cache != value) {
+        ZVAL_STRINGL(*cache, Z_STRVAL_P(value), Z_STRLEN_P(value), 1);
+      }
       break;
     }
     case UPB_TYPE_MESSAGE: {
-      // TODO(teboring): Add it back.
-      // if (CLASS_OF(value) == CLASS_OF(Qnil)) {
-      //   value = Qnil;
-      // } else if (CLASS_OF(value) != type_class) {
-      //   php_raise(php_eTypeError,
-      //            "Invalid type %s to assign to submessage field.",
-      //            php_class2name(CLASS_OF(value)));
-      // }
-      // DEREF(memory, VALUE) = value;
-      break;
-    }
-    case UPB_TYPE_ENUM: {
-      // TODO(teboring): Add it back.
-      // int32_t int_val = 0;
-      // if (!is_php_num(value) && TYPE(value) != T_SYMBOL) {
-      //   php_raise(php_eTypeError,
-      //            "Expected number or symbol type for enum field.");
-      // }
-      // if (TYPE(value) == T_SYMBOL) {
-      //   // Ensure that the given symbol exists in the enum module.
-      //   VALUE lookup = php_funcall(type_class, php_intern("resolve"), 1, value);
-      //   if (lookup == Qnil) {
-      //     php_raise(php_eRangeError, "Unknown symbol value for enum field.");
-      //   } else {
-      //     int_val = NUM2INT(lookup);
-      //   }
-      // } else {
-      //   native_slot_check_int_range_precision(UPB_TYPE_INT32, value);
-      //   int_val = NUM2INT(value);
-      // }
-      // DEREF(memory, int32_t) = int_val;
-      // break;
-    }
-    case UPB_TYPE_INT32:
-    case UPB_TYPE_INT64:
-    case UPB_TYPE_UINT32:
-    case UPB_TYPE_UINT64:
-      native_slot_check_int_range_precision(type, value);
-      switch (type) {
-        case UPB_TYPE_INT32:
-          php_printf("Setting INT32 field\n");
-          DEREF(memory, int32_t) = Z_LVAL_P(value);
-          break;
-        case UPB_TYPE_INT64:
-          // TODO(teboring): Add it back.
-          // DEREF(memory, int64_t) = NUM2LL(value);
-          break;
-        case UPB_TYPE_UINT32:
-          // TODO(teboring): Add it back.
-          // DEREF(memory, uint32_t) = NUM2UINT(value);
-          break;
-        case UPB_TYPE_UINT64:
-          // TODO(teboring): Add it back.
-          // DEREF(memory, uint64_t) = NUM2ULL(value);
-          break;
-        default:
-          break;
+      // Same as above for string/bytes fields.
+      zval* value = DEREF(memory, zval*);
+      if (*cache != value) {
+        ZVAL_ZVAL(*cache, value, 1, 0);
       }
-      break;
+      return;
+    }
     default:
-      break;
+      return EG(uninitialized_zval_ptr);
   }
+}
 
-  if (case_memory != NULL) {
-    *case_memory = case_number;
+void native_slot_get_default(upb_fieldtype_t type, zval** cache TSRMLS_DC) {
+  switch (type) {
+#define CASE(upb_type, php_type)     \
+  case UPB_TYPE_##upb_type:          \
+    SEPARATE_ZVAL_IF_NOT_REF(cache); \
+    ZVAL_##php_type(*cache, 0);      \
+    return;
+
+    CASE(FLOAT, DOUBLE)
+    CASE(DOUBLE, DOUBLE)
+    CASE(BOOL, BOOL)
+    CASE(INT32, LONG)
+    CASE(INT64, LONG)
+    CASE(UINT32, LONG)
+    CASE(UINT64, LONG)
+    CASE(ENUM, LONG)
+
+#undef CASE
+
+    case UPB_TYPE_STRING:
+    case UPB_TYPE_BYTES: {
+      SEPARATE_ZVAL_IF_NOT_REF(cache);
+      ZVAL_STRINGL(*cache, "", 0, 1);
+      break;
+    }
+    case UPB_TYPE_MESSAGE: {
+      SEPARATE_ZVAL_IF_NOT_REF(cache);
+      ZVAL_NULL(*cache);
+      return;
+    }
+    default:
+      return EG(uninitialized_zval_ptr);
   }
 }
 
@@ -281,6 +273,40 @@
   return tryget_map_entry_msgdef(field) != NULL;
 }
 
+const upb_fielddef* map_field_key(const upb_fielddef* field) {
+  const upb_msgdef* subdef = map_entry_msgdef(field);
+  return map_entry_key(subdef);
+}
+
+const upb_fielddef* map_field_value(const upb_fielddef* field) {
+  const upb_msgdef* subdef = map_entry_msgdef(field);
+  return map_entry_value(subdef);
+}
+
+const upb_fielddef* map_entry_key(const upb_msgdef* msgdef) {
+  const upb_fielddef* key_field = upb_msgdef_itof(msgdef, MAP_KEY_FIELD);
+  assert(key_field != NULL);
+  return key_field;
+}
+
+const upb_fielddef* map_entry_value(const upb_msgdef* msgdef) {
+  const upb_fielddef* value_field = upb_msgdef_itof(msgdef, MAP_VALUE_FIELD);
+  assert(value_field != NULL);
+  return value_field;
+}
+
+const zend_class_entry* field_type_class(const upb_fielddef* field) {
+  if (upb_fielddef_type(field) == UPB_TYPE_MESSAGE) {
+    zval* desc_php = get_def_obj(upb_fielddef_subdef(field));
+    Descriptor* desc = zend_object_store_get_object(desc_php TSRMLS_CC);
+    return desc->klass;
+  } else if (upb_fielddef_type(field) == UPB_TYPE_ENUM) {
+    zval* desc_php = get_def_obj(upb_fielddef_subdef(field));
+    EnumDescriptor* desc = zend_object_store_get_object(desc_php TSRMLS_CC);
+    return desc->klass;
+  }
+}
+
 // -----------------------------------------------------------------------------
 // Memory layout management.
 // -----------------------------------------------------------------------------
@@ -290,12 +316,29 @@
   return (offset + granularity - 1) & ~(granularity - 1);
 }
 
+static void* slot_memory(MessageLayout* layout, const void* storage,
+                         const upb_fielddef* field) {
+  return ((uint8_t*)storage) + layout->fields[upb_fielddef_index(field)].offset;
+}
+
+static uint32_t* slot_oneof_case(MessageLayout* layout, const void* storage,
+                                 const upb_fielddef* field) {
+  return (uint32_t*)(((uint8_t*)storage) +
+                     layout->fields[upb_fielddef_index(field)].case_offset);
+}
+
+static int slot_property_cache(MessageLayout* layout, const void* storage,
+                               const upb_fielddef* field) {
+  return layout->fields[upb_fielddef_index(field)].cache_index;
+}
+
 MessageLayout* create_layout(const upb_msgdef* msgdef) {
   MessageLayout* layout = ALLOC(MessageLayout);
   int nfields = upb_msgdef_numfields(msgdef);
   upb_msg_field_iter it;
   upb_msg_oneof_iter oit;
   size_t off = 0;
+  int i = 0;
 
   layout->fields = ALLOC_N(MessageField, nfields);
 
@@ -322,6 +365,7 @@
     layout->fields[upb_fielddef_index(field)].offset = off;
     layout->fields[upb_fielddef_index(field)].case_offset =
         MESSAGE_FIELD_NO_CASE;
+    layout->fields[upb_fielddef_index(field)].cache_index = i++;
     off += field_size;
   }
 
@@ -353,11 +397,13 @@
          upb_oneof_next(&fit)) {
       const upb_fielddef* field = upb_oneof_iter_field(&fit);
       layout->fields[upb_fielddef_index(field)].offset = off;
+      layout->fields[upb_fielddef_index(field)].cache_index = i;
     }
+    i++;
     off += field_size;
   }
 
-  // Now the case fields.
+  // Now the case offset.
   for (upb_msg_oneof_begin(&oit, msgdef); !upb_msg_oneof_done(&oit);
        upb_msg_oneof_next(&oit)) {
     const upb_oneofdef* oneof = upb_msg_iter_oneof(&oit);
@@ -389,151 +435,125 @@
   FREE(layout);
 }
 
-// TODO(teboring): Add it back.
-// VALUE field_type_class(const upb_fielddef* field) {
-//   VALUE type_class = Qnil;
-//   if (upb_fielddef_type(field) == UPB_TYPE_MESSAGE) {
-//     VALUE submsgdesc = get_def_obj(upb_fielddef_subdef(field));
-//     type_class = Descriptor_msgclass(submsgdesc);
-//   } else if (upb_fielddef_type(field) == UPB_TYPE_ENUM) {
-//     VALUE subenumdesc = get_def_obj(upb_fielddef_subdef(field));
-//     type_class = EnumDescriptor_enummodule(subenumdesc);
-//   }
-//   return type_class;
-// }
-
-static void* slot_memory(MessageLayout* layout, const void* storage,
-                         const upb_fielddef* field) {
-  return ((uint8_t*)storage) + layout->fields[upb_fielddef_index(field)].offset;
-}
-
-static uint32_t* slot_oneof_case(MessageLayout* layout, const void* storage,
-                                 const upb_fielddef* field) {
-  return (uint32_t*)(((uint8_t*)storage) +
-                     layout->fields[upb_fielddef_index(field)].case_offset);
-}
-
-void layout_set(MessageLayout* layout, void* storage, const upb_fielddef* field,
-                zval* val) {
-  void* memory = slot_memory(layout, storage, field);
-  uint32_t* oneof_case = slot_oneof_case(layout, storage, field);
-
-  if (upb_fielddef_containingoneof(field)) {
-    if (Z_TYPE_P(val) == IS_NULL) {
-      // Assigning nil to a oneof field clears the oneof completely.
-      *oneof_case = ONEOF_CASE_NONE;
-      memset(memory, 0, NATIVE_SLOT_MAX_SIZE);
-    } else {
-      // The transition between field types for a single oneof (union) slot is
-      // somewhat complex because we need to ensure that a GC triggered at any
-      // point by a call into the Ruby VM sees a valid state for this field and
-      // does not either go off into the weeds (following what it thinks is a
-      // VALUE but is actually a different field type) or miss an object (seeing
-      // what it thinks is a primitive field but is actually a VALUE for the new
-      // field type).
-      //
-      // In order for the transition to be safe, the oneof case slot must be in
-      // sync with the value slot whenever the Ruby VM has been called. Thus, we
-      // use native_slot_set_value_and_case(), which ensures that both the value
-      // and case number are altered atomically (w.r.t. the Ruby VM).
-      native_slot_set_value_and_case(upb_fielddef_type(field),
-                                     /*field_type_class(field),*/ memory, val,
-                                     oneof_case, upb_fielddef_number(field));
-    }
-  } else if (is_map_field(field)) {
-    // TODO(teboring): Add it back.
-    // check_map_field_type(val, field);
-    // DEREF(memory, zval*) = val;
-  } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
-    // TODO(teboring): Add it back.
-    // check_repeated_field_type(val, field);
-    // DEREF(memory, zval*) = val;
-  } else {
-    native_slot_set(upb_fielddef_type(field), /*field_type_class(field),*/ memory,
-                    val);
-  }
-}
-
-void layout_init(MessageLayout* layout, void* storage) {
+void layout_init(MessageLayout* layout, void* storage, zval** properties_table) {
+  int i;
   upb_msg_field_iter it;
-  for (upb_msg_field_begin(&it, layout->msgdef); !upb_msg_field_done(&it);
-       upb_msg_field_next(&it)) {
+  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);
+    zval** property_ptr = &properties_table[cache_index];
 
     if (upb_fielddef_containingoneof(field)) {
-      // TODO(teboring): Add it back.
-      // memset(memory, 0, NATIVE_SLOT_MAX_SIZE);
-      // *oneof_case = ONEOF_CASE_NONE;
+      memset(memory, 0, NATIVE_SLOT_MAX_SIZE);
+      *oneof_case = ONEOF_CASE_NONE;
     } else if (is_map_field(field)) {
-      // TODO(teboring): Add it back.
-      // VALUE map = Qnil;
-
-      // const upb_fielddef* key_field = map_field_key(field);
-      // const upb_fielddef* value_field = map_field_value(field);
-      // VALUE type_class = field_type_class(value_field);
-
-      // if (type_class != Qnil) {
-      //   VALUE args[3] = {
-      //       fieldtype_to_php(upb_fielddef_type(key_field)),
-      //       fieldtype_to_php(upb_fielddef_type(value_field)), type_class,
-      //   };
-      //   map = php_class_new_instance(3, args, cMap);
-      // } else {
-      //   VALUE args[2] = {
-      //       fieldtype_to_php(upb_fielddef_type(key_field)),
-      //       fieldtype_to_php(upb_fielddef_type(value_field)),
-      //   };
-      //   map = php_class_new_instance(2, args, cMap);
-      // }
-
-      // DEREF(memory, VALUE) = map;
+      zval_ptr_dtor(property_ptr);
+      map_field_create_with_type(map_field_type, field, property_ptr);
+      DEREF(memory, zval**) = property_ptr;
     } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
-      // TODO(teboring): Add it back.
-      // VALUE ary = Qnil;
-
-      // VALUE type_class = field_type_class(field);
-
-      // if (type_class != Qnil) {
-      //   VALUE args[2] = {
-      //       fieldtype_to_php(upb_fielddef_type(field)), type_class,
-      //   };
-      //   ary = php_class_new_instance(2, args, cRepeatedField);
-      // } else {
-      //   VALUE args[1] = {fieldtype_to_php(upb_fielddef_type(field))};
-      //   ary = php_class_new_instance(1, args, cRepeatedField);
-      // }
-
-      // DEREF(memory, VALUE) = ary;
+      zval_ptr_dtor(property_ptr);
+      repeated_field_create_with_type(repeated_field_type, field, property_ptr);
+      DEREF(memory, zval**) = property_ptr;
     } else {
-      native_slot_init(upb_fielddef_type(field), memory);
+      native_slot_init(upb_fielddef_type(field), memory, property_ptr);
     }
   }
 }
 
+// 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)) {
+    case UPB_TYPE_STRING:
+    case UPB_TYPE_BYTES:
+    case UPB_TYPE_MESSAGE:
+      memory = DEREF(memory, zval**);
+      break;
+    default:
+      // No operation
+      break;
+  }
+  return memory;
+}
+
 zval* layout_get(MessageLayout* layout, const void* storage,
-                 const upb_fielddef* field TSRMLS_DC) {
+                 const upb_fielddef* field, zval** cache TSRMLS_DC) {
   void* memory = slot_memory(layout, storage, field);
   uint32_t* oneof_case = slot_oneof_case(layout, storage, field);
 
   if (upb_fielddef_containingoneof(field)) {
     if (*oneof_case != upb_fielddef_number(field)) {
-      return NULL;
-      // TODO(teboring): Add it back.
-      // return Qnil;
+      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);
     }
-      return NULL;
-    // TODO(teboring): Add it back.
-    // return native_slot_get(upb_fielddef_type(field), field_type_class(field),
-    //                        memory);
+    return *cache;
   } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
-      return NULL;
-    // TODO(teboring): Add it back.
-    // return *((VALUE*)memory);
+    return *cache;
   } else {
-    return native_slot_get(
-        upb_fielddef_type(field), /*field_type_class(field), */
-        memory TSRMLS_CC);
+    native_slot_get(upb_fielddef_type(field), value_memory(field, memory),
+                    cache TSRMLS_CC);
+    return *cache;
+  }
+}
+
+void layout_set(MessageLayout* layout, MessageHeader* header, const upb_fielddef* field,
+                zval* val) {
+  void* storage = message_data(header);
+  void* memory = slot_memory(layout, storage, field);
+  uint32_t* oneof_case = slot_oneof_case(layout, storage, field);
+
+  if (upb_fielddef_containingoneof(field)) {
+    upb_fieldtype_t type = upb_fielddef_type(field);
+    zend_class_entry *ce = NULL;
+
+    // For non-singular fields, the related memory needs to point to the actual
+    // zval in properties table first.
+    switch (type) {
+      case UPB_TYPE_MESSAGE: {
+        upb_msgdef* msg = upb_fielddef_msgsubdef(field);
+        zval* desc_php = get_def_obj(msg);
+        Descriptor* desc = zend_object_store_get_object(desc_php TSRMLS_CC);
+        ce = desc->klass;
+        // Intentionally fall through.
+      }
+      case UPB_TYPE_STRING:
+      case UPB_TYPE_BYTES: {
+        int property_cache_index =
+            header->descriptor->layout->fields[upb_fielddef_index(field)]
+                .cache_index;
+        DEREF(memory, zval**) =
+            &(header->std.properties_table)[property_cache_index];
+        memory = DEREF(memory, zval**);
+        break;
+      }
+      default:
+        break;
+    }
+
+    native_slot_set(type, ce, memory, val);
+    *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, zval**);
+    if (EXPECTED(DEREF(memory, zval*) != val)) {
+        zval_ptr_dtor(memory);
+        DEREF(memory, zval*) = val;
+        Z_ADDREF_P(val);
+    }
+  } else {
+    upb_fieldtype_t type = upb_fielddef_type(field);
+    zend_class_entry *ce = NULL;
+    if (type == UPB_TYPE_MESSAGE) {
+      upb_msgdef* msg = upb_fielddef_msgsubdef(field);
+      zval* desc_php = get_def_obj(msg);
+      Descriptor* desc = zend_object_store_get_object(desc_php TSRMLS_CC);
+      ce = desc->klass;
+    }
+    native_slot_set(type, ce, value_memory(field, memory), val);
   }
 }
diff --git a/php/ext/google/protobuf/test.php b/php/ext/google/protobuf/test.php
deleted file mode 100644
index 6bbadd4..0000000
--- a/php/ext/google/protobuf/test.php
+++ /dev/null
@@ -1,15 +0,0 @@
-<?php
-
-
-namespace Google\Protobuf;
-
-$pool = get_generated_pool();
-$pool->addMessage("TestMessage")
-    ->optional("optional_int32_a", "int32", 1)
-    ->optional("optional_int32_b", "int32", 2)
-    ->finalizeToPool()
-    ->finalize();
-
-$test_message = new \TestMessage();
-
-?>
diff --git a/php/ext/google/protobuf/type_check.c b/php/ext/google/protobuf/type_check.c
new file mode 100644
index 0000000..fe9359f
--- /dev/null
+++ b/php/ext/google/protobuf/type_check.c
@@ -0,0 +1,310 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <Zend/zend_operators.h>
+
+#include "protobuf.h"
+#include "utf8.h"
+
+static zend_class_entry* util_type;
+
+ZEND_BEGIN_ARG_INFO_EX(arg_check_optional, 0, 0, 1)
+  ZEND_ARG_INFO(1, val)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arg_check_message, 0, 0, 2)
+  ZEND_ARG_INFO(1, val)
+  ZEND_ARG_INFO(0, klass)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arg_check_repeated, 0, 0, 2)
+  ZEND_ARG_INFO(1, val)
+  ZEND_ARG_INFO(0, type)
+  ZEND_ARG_INFO(0, klass)
+ZEND_END_ARG_INFO()
+
+static zend_function_entry util_methods[] = {
+  PHP_ME(Util, checkInt32,  arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+  PHP_ME(Util, checkUint32, arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+  PHP_ME(Util, checkInt64,  arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+  PHP_ME(Util, checkUint64, arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+  PHP_ME(Util, checkEnum,   arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+  PHP_ME(Util, checkFloat,  arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+  PHP_ME(Util, checkDouble, arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+  PHP_ME(Util, checkBool,   arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+  PHP_ME(Util, checkString, arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+  PHP_ME(Util, checkBytes,  arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+  PHP_ME(Util, checkMessage, arg_check_message, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+  PHP_ME(Util, checkRepeatedField, arg_check_repeated,
+         ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+  ZEND_FE_END
+};
+
+void util_init(TSRMLS_D) {
+  zend_class_entry class_type;
+  INIT_CLASS_ENTRY(class_type, "Google\\Protobuf\\Internal\\GPBUtil",
+                   util_methods);
+  util_type = zend_register_internal_class(&class_type TSRMLS_CC);
+}
+
+// -----------------------------------------------------------------------------
+// Type checking/conversion.
+// -----------------------------------------------------------------------------
+
+#define CONVERT_TO_INTEGER(type)                                             \
+  static bool convert_long_to_##type(long val, type##_t* type##_value) {     \
+    *type##_value = (type##_t)val;                                           \
+    return true;                                                             \
+  }                                                                          \
+                                                                             \
+  static bool convert_double_to_##type(double val, type##_t* type##_value) { \
+    *type##_value = (type##_t)zend_dval_to_lval(val);                        \
+    return true;                                                             \
+  }                                                                          \
+                                                                             \
+  static bool convert_string_to_##type(const char* val, int len,             \
+                                       type##_t* type##_value) {             \
+    long lval;                                                               \
+    double dval;                                                             \
+                                                                             \
+    switch (is_numeric_string(val, len, &lval, &dval, 0)) {                  \
+      case IS_DOUBLE: {                                                      \
+        return convert_double_to_##type(dval, type##_value);                 \
+      }                                                                      \
+      case IS_LONG: {                                                        \
+        return convert_long_to_##type(lval, type##_value);                   \
+      }                                                                      \
+      default:                                                               \
+        zend_error(E_USER_ERROR,                                             \
+                   "Given string value cannot be converted to integer.");    \
+        return false;                                                        \
+    }                                                                        \
+  }                                                                          \
+                                                                             \
+  bool protobuf_convert_to_##type(zval* from, type##_t* to) {                \
+    switch (Z_TYPE_P(from)) {                                                \
+      case IS_LONG: {                                                        \
+        return convert_long_to_##type(Z_LVAL_P(from), to);                   \
+      }                                                                      \
+      case IS_DOUBLE: {                                                      \
+        return convert_double_to_##type(Z_DVAL_P(from), to);                 \
+      }                                                                      \
+      case IS_STRING: {                                                      \
+        return convert_string_to_##type(Z_STRVAL_P(from), Z_STRLEN_P(from),  \
+                                        to);                                 \
+      }                                                                      \
+      default: {                                                             \
+        zend_error(E_USER_ERROR,                                             \
+                   "Given value cannot be converted to integer.");           \
+        return false;                                                        \
+      }                                                                      \
+    }                                                                        \
+    return false;                                                            \
+  }
+
+CONVERT_TO_INTEGER(int32);
+CONVERT_TO_INTEGER(uint32);
+CONVERT_TO_INTEGER(int64);
+CONVERT_TO_INTEGER(uint64);
+
+#undef CONVERT_TO_INTEGER
+
+#define CONVERT_TO_FLOAT(type)                                              \
+  static bool convert_long_to_##type(long val, type* type##_value) {        \
+    *type##_value = (type)val;                                              \
+    return true;                                                            \
+  }                                                                         \
+                                                                            \
+  static bool convert_double_to_##type(double val, type* type##_value) {    \
+    *type##_value = (type)val;                                              \
+    return true;                                                            \
+  }                                                                         \
+                                                                            \
+  static bool convert_string_to_##type(const char* val, int len,            \
+                                       type* type##_value) {                \
+    long lval;                                                              \
+    double dval;                                                            \
+                                                                            \
+    switch (is_numeric_string(val, len, &lval, &dval, 0)) {                 \
+      case IS_DOUBLE: {                                                     \
+        *type##_value = (type)dval;                                         \
+        return true;                                                        \
+      }                                                                     \
+      case IS_LONG: {                                                       \
+        *type##_value = (type)lval;                                         \
+        return true;                                                        \
+      }                                                                     \
+      default:                                                              \
+        zend_error(E_USER_ERROR,                                            \
+                   "Given string value cannot be converted to integer.");   \
+        return false;                                                       \
+    }                                                                       \
+  }                                                                         \
+                                                                            \
+  bool protobuf_convert_to_##type(zval* from, type* to) {                   \
+    switch (Z_TYPE_P(from)) {                                               \
+      case IS_LONG: {                                                       \
+        return convert_long_to_##type(Z_LVAL_P(from), to);                  \
+      }                                                                     \
+      case IS_DOUBLE: {                                                     \
+        return convert_double_to_##type(Z_DVAL_P(from), to);                \
+      }                                                                     \
+      case IS_STRING: {                                                     \
+        return convert_string_to_##type(Z_STRVAL_P(from), Z_STRLEN_P(from), \
+                                        to);                                \
+      }                                                                     \
+      default: {                                                            \
+        zend_error(E_USER_ERROR,                                            \
+                   "Given value cannot be converted to integer.");          \
+        return false;                                                       \
+      }                                                                     \
+    }                                                                       \
+    return false;                                                           \
+  }
+
+CONVERT_TO_FLOAT(float);
+CONVERT_TO_FLOAT(double);
+
+#undef CONVERT_TO_FLOAT
+
+bool protobuf_convert_to_bool(zval* from, int8_t* to) {
+  switch (Z_TYPE_P(from)) {
+    case IS_BOOL:
+      *to = (int8_t)Z_BVAL_P(from);
+      break;
+    case IS_LONG:
+      *to = (int8_t)(Z_LVAL_P(from) != 0);
+      break;
+    case IS_DOUBLE:
+      *to = (int8_t)(Z_LVAL_P(from) != 0);
+      break;
+    case IS_STRING: {
+      char* strval = Z_STRVAL_P(from);
+
+      if (Z_STRLEN_P(from) == 0 ||
+          (Z_STRLEN_P(from) == 1 && Z_STRVAL_P(from)[0] == '0')) {
+        *to = 0;
+      } else {
+        *to = 1;
+      }
+      STR_FREE(strval);
+    } break;
+    default: {
+      zend_error(E_USER_ERROR, "Given value cannot be converted to bool.");
+      return false;
+    }
+  }
+  return true;
+}
+
+bool protobuf_convert_to_string(zval* from) {
+  switch (Z_TYPE_P(from)) {
+    case IS_STRING: {
+      return true;
+    }
+    case IS_BOOL:
+    case IS_LONG:
+    case IS_DOUBLE: {
+      int use_copy;
+      zval tmp;
+      zend_make_printable_zval(from, &tmp, &use_copy);
+      ZVAL_COPY_VALUE(from, &tmp);
+      return true;
+    }
+    default:
+      zend_error(E_USER_ERROR, "Given value cannot be converted to string.");
+      return false;
+  }
+}
+
+// -----------------------------------------------------------------------------
+// PHP Functions.
+// -----------------------------------------------------------------------------
+
+// The implementation of type checking for primitive fields is empty. This is
+// because type checking is done when direct assigning message fields (e.g.,
+// foo->a = 1). Functions defined here are place holders in generated code for
+// pure PHP implementation (c extension and pure PHP share the same generated
+// code).
+#define PHP_TYPE_CHECK(type) \
+  PHP_METHOD(Util, check##type) {}
+
+PHP_TYPE_CHECK(Int32)
+PHP_TYPE_CHECK(Uint32)
+PHP_TYPE_CHECK(Int64)
+PHP_TYPE_CHECK(Uint64)
+PHP_TYPE_CHECK(Enum)
+PHP_TYPE_CHECK(Float)
+PHP_TYPE_CHECK(Double)
+PHP_TYPE_CHECK(Bool)
+PHP_TYPE_CHECK(String)
+PHP_TYPE_CHECK(Bytes)
+
+#undef PHP_TYPE_CHECK
+
+PHP_METHOD(Util, checkMessage) {
+  zval* val;
+  zend_class_entry* klass = NULL;
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o!C", &val, &klass) ==
+      FAILURE) {
+    return;
+  }
+  if (val == NULL) {
+    RETURN_NULL();
+  }
+  if (!instanceof_function(Z_OBJCE_P(val), klass TSRMLS_CC)) {
+    zend_error(E_USER_ERROR, "Given value is not an instance of %s.",
+               klass->name);
+    return;
+  }
+  RETURN_ZVAL(val, 1, 0);
+}
+
+PHP_METHOD(Util, checkRepeatedField) {
+  zval* val;
+  long type;
+  const zend_class_entry* klass = NULL;
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Ol|C", &val,
+                            repeated_field_type, &type, &klass) == FAILURE) {
+    return;
+  }
+
+  RepeatedField *intern =
+      (RepeatedField *)zend_object_store_get_object(val TSRMLS_CC);
+  if (to_fieldtype(type) != intern->type) {
+    zend_error(E_USER_ERROR, "Incorrect repeated field type.");
+    return;
+  }
+  if (klass != NULL && intern->msg_ce != klass) {
+    zend_error(E_USER_ERROR, "Expect a repeated field of %s, but %s is given.",
+               klass->name, intern->msg_ce->name);
+    return;
+  }
+}
diff --git a/php/ext/google/protobuf/upb.c b/php/ext/google/protobuf/upb.c
index 048a163..98daafc 100644
--- a/php/ext/google/protobuf/upb.c
+++ b/php/ext/google/protobuf/upb.c
@@ -1,7 +1,37 @@
-// Amalgamated source file
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
 #include "upb.h"
 
 
+#include <ctype.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -11,7 +41,7 @@
 } str_t;
 
 static str_t *newstr(const char *data, size_t len) {
-  str_t *ret = malloc(sizeof(*ret) + len);
+  str_t *ret = upb_gmalloc(sizeof(*ret) + len);
   if (!ret) return NULL;
   ret->len = len;
   memcpy(ret->str, data, len);
@@ -19,7 +49,7 @@
   return ret;
 }
 
-static void freestr(str_t *s) { free(s); }
+static void freestr(str_t *s) { upb_gfree(s); }
 
 /* isalpha() etc. from <ctype.h> are locale-dependent, which we don't want. */
 static bool upb_isbetween(char c, char low, char high) {
@@ -64,6 +94,22 @@
   return !start;
 }
 
+static bool upb_isoneof(const upb_refcounted *def) {
+  return def->vtbl == &upb_oneofdef_vtbl;
+}
+
+static bool upb_isfield(const upb_refcounted *def) {
+  return def->vtbl == &upb_fielddef_vtbl;
+}
+
+static const upb_oneofdef *upb_trygetoneof(const upb_refcounted *def) {
+  return upb_isoneof(def) ? (const upb_oneofdef*)def : NULL;
+}
+
+static const upb_fielddef *upb_trygetfield(const upb_refcounted *def) {
+  return upb_isfield(def) ? (const upb_fielddef*)def : NULL;
+}
+
 
 /* upb_def ********************************************************************/
 
@@ -71,14 +117,39 @@
 
 const char *upb_def_fullname(const upb_def *d) { return d->fullname; }
 
+const char *upb_def_name(const upb_def *d) {
+  const char *p;
+
+  if (d->fullname == NULL) {
+    return NULL;
+  } else if ((p = strrchr(d->fullname, '.')) == NULL) {
+    /* No '.' in the name, return the full string. */
+    return d->fullname;
+  } else {
+    /* Return one past the last '.'. */
+    return p + 1;
+  }
+}
+
 bool upb_def_setfullname(upb_def *def, const char *fullname, upb_status *s) {
-  assert(!upb_def_isfrozen(def));
-  if (!upb_isident(fullname, strlen(fullname), true, s)) return false;
-  free((void*)def->fullname);
-  def->fullname = upb_strdup(fullname);
+  UPB_ASSERT(!upb_def_isfrozen(def));
+  if (!upb_isident(fullname, strlen(fullname), true, s)) {
+    return false;
+  }
+
+  fullname = upb_gstrdup(fullname);
+  if (!fullname) {
+    upb_upberr_setoom(s);
+    return false;
+  }
+
+  upb_gfree((void*)def->fullname);
+  def->fullname = fullname;
   return true;
 }
 
+const upb_filedef *upb_def_file(const upb_def *d) { return d->file; }
+
 upb_def *upb_def_dup(const upb_def *def, const void *o) {
   switch (def->type) {
     case UPB_DEF_MSG:
@@ -90,7 +161,7 @@
     case UPB_DEF_ENUM:
       return upb_enumdef_upcast_mutable(
           upb_enumdef_dup(upb_downcast_enumdef(def), o));
-    default: assert(false); return NULL;
+    default: UPB_ASSERT(false); return NULL;
   }
 }
 
@@ -101,11 +172,12 @@
   def->type = type;
   def->fullname = NULL;
   def->came_from_user = false;
+  def->file = NULL;
   return true;
 }
 
 static void upb_def_uninit(upb_def *def) {
-  free((void*)def->fullname);
+  upb_gfree((void*)def->fullname);
 }
 
 static const char *msgdef_name(const upb_msgdef *m) {
@@ -160,14 +232,14 @@
     bool has_default_number = upb_fielddef_enumhasdefaultint32(f);
 
     /* Previously verified by upb_validate_enumdef(). */
-    assert(upb_enumdef_numvals(upb_fielddef_enumsubdef(f)) > 0);
+    UPB_ASSERT(upb_enumdef_numvals(upb_fielddef_enumsubdef(f)) > 0);
 
     /* We've already validated that we have an associated enumdef and that it
      * has at least one member, so at least one of these should be true.
      * Because if the user didn't set anything, we'll pick up the enum's
      * default, but if the user *did* set something we should at least pick up
      * the one they set (int32 or string). */
-    assert(has_default_name || has_default_number);
+    UPB_ASSERT(has_default_name || has_default_number);
 
     if (!has_default_name) {
       upb_status_seterrf(s,
@@ -223,7 +295,7 @@
 uint32_t field_rank(const upb_fielddef *f) {
   uint32_t ret = upb_fielddef_number(f);
   const uint32_t high_bit = 1 << 30;
-  assert(ret < high_bit);
+  UPB_ASSERT(ret < high_bit);
   if (!upb_fielddef_issubmsg(f))
     ret |= high_bit;
   return ret;
@@ -242,17 +314,28 @@
   int i;
   uint32_t selector;
   int n = upb_msgdef_numfields(m);
-  upb_fielddef **fields = malloc(n * sizeof(*fields));
-  if (!fields) return false;
+  upb_fielddef **fields;
+
+  if (n == 0) {
+    m->selector_count = UPB_STATIC_SELECTOR_COUNT;
+    m->submsg_field_count = 0;
+    return true;
+  }
+
+  fields = upb_gmalloc(n * sizeof(*fields));
+  if (!fields) {
+    upb_upberr_setoom(s);
+    return false;
+  }
 
   m->submsg_field_count = 0;
   for(i = 0, upb_msg_field_begin(&j, m);
       !upb_msg_field_done(&j);
       upb_msg_field_next(&j), i++) {
     upb_fielddef *f = upb_msg_iter_field(&j);
-    assert(f->msg.def == m);
+    UPB_ASSERT(f->msg.def == m);
     if (!upb_validate_field(f, s)) {
-      free(fields);
+      upb_gfree(fields);
       return false;
     }
     if (upb_fielddef_issubmsg(f)) {
@@ -312,15 +395,12 @@
 #undef TRY
 #endif
 
-  free(fields);
+  upb_gfree(fields);
   return true;
 }
 
-bool upb_def_freeze(upb_def *const* defs, int n, upb_status *s) {
-  int i;
-  int maxdepth;
-  bool ret;
-  upb_status_clear(s);
+bool _upb_def_validate(upb_def *const*defs, size_t n, upb_status *s) {
+  size_t i;
 
   /* First perform validation, in two passes so we can check that we have a
    * transitive closure without needing to search. */
@@ -346,8 +426,9 @@
   /* Second pass of validation.  Also assign selector bases and indexes, and
    * compact tables. */
   for (i = 0; i < n; i++) {
-    upb_msgdef *m = upb_dyncast_msgdef_mutable(defs[i]);
-    upb_enumdef *e = upb_dyncast_enumdef_mutable(defs[i]);
+    upb_def *def = defs[i];
+    upb_msgdef *m = upb_dyncast_msgdef_mutable(def);
+    upb_enumdef *e = upb_dyncast_enumdef_mutable(def);
     if (m) {
       upb_inttable_compact(&m->itof);
       if (!assign_msg_indices(m, s)) {
@@ -358,46 +439,68 @@
     }
   }
 
-  /* Def graph contains FieldDefs between each MessageDef, so double the
-   * limit. */
-  maxdepth = UPB_MAX_MESSAGE_DEPTH * 2;
-
-  /* Validation all passed; freeze the defs. */
-  ret = upb_refcounted_freeze((upb_refcounted * const *)defs, n, s, maxdepth);
-  assert(!(s && ret != upb_ok(s)));
-  return ret;
+  return true;
 
 err:
   for (i = 0; i < n; i++) {
-    defs[i]->came_from_user = false;
+    upb_def *def = defs[i];
+    def->came_from_user = false;
   }
-  assert(!(s && upb_ok(s)));
+  UPB_ASSERT(!(s && upb_ok(s)));
   return false;
 }
 
+bool upb_def_freeze(upb_def *const* defs, size_t n, upb_status *s) {
+  /* Def graph contains FieldDefs between each MessageDef, so double the
+   * limit. */
+  const size_t maxdepth = UPB_MAX_MESSAGE_DEPTH * 2;
+
+  if (!_upb_def_validate(defs, n, s)) {
+    return false;
+  }
+
+
+  /* Validation all passed; freeze the objects. */
+  return upb_refcounted_freeze((upb_refcounted *const*)defs, n, s, maxdepth);
+}
+
 
 /* upb_enumdef ****************************************************************/
 
-static void upb_enumdef_free(upb_refcounted *r) {
+static void visitenum(const upb_refcounted *r, upb_refcounted_visit *visit,
+                      void *closure) {
+  const upb_enumdef *e = (const upb_enumdef*)r;
+  const upb_def *def = upb_enumdef_upcast(e);
+  if (upb_def_file(def)) {
+    visit(r, upb_filedef_upcast(upb_def_file(def)), closure);
+  }
+}
+
+static void freeenum(upb_refcounted *r) {
   upb_enumdef *e = (upb_enumdef*)r;
   upb_inttable_iter i;
   upb_inttable_begin(&i, &e->iton);
   for( ; !upb_inttable_done(&i); upb_inttable_next(&i)) {
-    /* To clean up the upb_strdup() from upb_enumdef_addval(). */
-    free(upb_value_getcstr(upb_inttable_iter_value(&i)));
+    /* To clean up the upb_gstrdup() from upb_enumdef_addval(). */
+    upb_gfree(upb_value_getcstr(upb_inttable_iter_value(&i)));
   }
   upb_strtable_uninit(&e->ntoi);
   upb_inttable_uninit(&e->iton);
   upb_def_uninit(upb_enumdef_upcast_mutable(e));
-  free(e);
+  upb_gfree(e);
 }
 
+const struct upb_refcounted_vtbl upb_enumdef_vtbl = {&visitenum, &freeenum};
+
 upb_enumdef *upb_enumdef_new(const void *owner) {
-  static const struct upb_refcounted_vtbl vtbl = {NULL, &upb_enumdef_free};
-  upb_enumdef *e = malloc(sizeof(*e));
+  upb_enumdef *e = upb_gmalloc(sizeof(*e));
   if (!e) return NULL;
-  if (!upb_def_init(upb_enumdef_upcast_mutable(e), UPB_DEF_ENUM, &vtbl, owner))
+
+  if (!upb_def_init(upb_enumdef_upcast_mutable(e), UPB_DEF_ENUM,
+                    &upb_enumdef_vtbl, owner)) {
     goto err2;
+  }
+
   if (!upb_strtable_init(&e->ntoi, UPB_CTYPE_INT32)) goto err2;
   if (!upb_inttable_init(&e->iton, UPB_CTYPE_CSTR)) goto err1;
   return e;
@@ -405,7 +508,7 @@
 err1:
   upb_strtable_uninit(&e->ntoi);
 err2:
-  free(e);
+  upb_gfree(e);
   return NULL;
 }
 
@@ -433,6 +536,10 @@
   return upb_def_fullname(upb_enumdef_upcast(e));
 }
 
+const char *upb_enumdef_name(const upb_enumdef *e) {
+  return upb_def_name(upb_enumdef_upcast(e));
+}
+
 bool upb_enumdef_setfullname(upb_enumdef *e, const char *fullname,
                              upb_status *s) {
   return upb_def_setfullname(upb_enumdef_upcast_mutable(e), fullname, s);
@@ -440,37 +547,46 @@
 
 bool upb_enumdef_addval(upb_enumdef *e, const char *name, int32_t num,
                         upb_status *status) {
+  char *name2;
+
   if (!upb_isident(name, strlen(name), false, status)) {
     return false;
   }
+
   if (upb_enumdef_ntoiz(e, name, NULL)) {
     upb_status_seterrf(status, "name '%s' is already defined", name);
     return false;
   }
+
   if (!upb_strtable_insert(&e->ntoi, name, upb_value_int32(num))) {
     upb_status_seterrmsg(status, "out of memory");
     return false;
   }
-  if (!upb_inttable_lookup(&e->iton, num, NULL) &&
-      !upb_inttable_insert(&e->iton, num, upb_value_cstr(upb_strdup(name)))) {
-    upb_status_seterrmsg(status, "out of memory");
-    upb_strtable_remove(&e->ntoi, name, NULL);
-    return false;
+
+  if (!upb_inttable_lookup(&e->iton, num, NULL)) {
+    name2 = upb_gstrdup(name);
+    if (!name2 || !upb_inttable_insert(&e->iton, num, upb_value_cstr(name2))) {
+      upb_status_seterrmsg(status, "out of memory");
+      upb_strtable_remove(&e->ntoi, name, NULL);
+      return false;
+    }
   }
+
   if (upb_enumdef_numvals(e) == 1) {
     bool ok = upb_enumdef_setdefault(e, num, NULL);
-    UPB_ASSERT_VAR(ok, ok);
+    UPB_ASSERT(ok);
   }
+
   return true;
 }
 
 int32_t upb_enumdef_default(const upb_enumdef *e) {
-  assert(upb_enumdef_iton(e, e->defaultval));
+  UPB_ASSERT(upb_enumdef_iton(e, e->defaultval));
   return e->defaultval;
 }
 
 bool upb_enumdef_setdefault(upb_enumdef *e, int32_t val, upb_status *s) {
-  assert(!upb_enumdef_isfrozen(e));
+  UPB_ASSERT(!upb_enumdef_isfrozen(e));
   if (!upb_enumdef_iton(e, val)) {
     upb_status_seterrf(s, "number '%d' is not in the enum.", val);
     return false;
@@ -525,32 +641,40 @@
     freestr(f->defaultval.bytes);
 }
 
+const char *upb_fielddef_fullname(const upb_fielddef *e) {
+  return upb_def_fullname(upb_fielddef_upcast(e));
+}
+
 static void visitfield(const upb_refcounted *r, upb_refcounted_visit *visit,
                        void *closure) {
   const upb_fielddef *f = (const upb_fielddef*)r;
+  const upb_def *def = upb_fielddef_upcast(f);
   if (upb_fielddef_containingtype(f)) {
     visit(r, upb_msgdef_upcast2(upb_fielddef_containingtype(f)), closure);
   }
   if (upb_fielddef_containingoneof(f)) {
-    visit(r, upb_oneofdef_upcast2(upb_fielddef_containingoneof(f)), closure);
+    visit(r, upb_oneofdef_upcast(upb_fielddef_containingoneof(f)), closure);
   }
   if (upb_fielddef_subdef(f)) {
     visit(r, upb_def_upcast(upb_fielddef_subdef(f)), closure);
   }
+  if (upb_def_file(def)) {
+    visit(r, upb_filedef_upcast(upb_def_file(def)), closure);
+  }
 }
 
 static void freefield(upb_refcounted *r) {
   upb_fielddef *f = (upb_fielddef*)r;
   upb_fielddef_uninit_default(f);
   if (f->subdef_is_symbolic)
-    free(f->sub.name);
+    upb_gfree(f->sub.name);
   upb_def_uninit(upb_fielddef_upcast_mutable(f));
-  free(f);
+  upb_gfree(f);
 }
 
 static const char *enumdefaultstr(const upb_fielddef *f) {
   const upb_enumdef *e;
-  assert(f->type_is_set_ && f->type_ == UPB_TYPE_ENUM);
+  UPB_ASSERT(f->type_is_set_ && f->type_ == UPB_TYPE_ENUM);
   e = upb_fielddef_enumsubdef(f);
   if (f->default_is_string && f->defaultval.bytes) {
     /* Default was explicitly set as a string. */
@@ -567,7 +691,7 @@
       /* Default is completely unset; pull enumdef default. */
       if (upb_enumdef_numvals(e) > 0) {
         const char *name = upb_enumdef_iton(e, upb_enumdef_default(e));
-        assert(name);
+        UPB_ASSERT(name);
         return name;
       }
     }
@@ -577,7 +701,7 @@
 
 static bool enumdefaultint32(const upb_fielddef *f, int32_t *val) {
   const upb_enumdef *e;
-  assert(f->type_is_set_ && f->type_ == UPB_TYPE_ENUM);
+  UPB_ASSERT(f->type_is_set_ && f->type_ == UPB_TYPE_ENUM);
   e = upb_fielddef_enumsubdef(f);
   if (!f->default_is_string) {
     /* Default was explicitly set as an integer. */
@@ -601,12 +725,14 @@
   return false;
 }
 
+const struct upb_refcounted_vtbl upb_fielddef_vtbl = {visitfield, freefield};
+
 upb_fielddef *upb_fielddef_new(const void *o) {
-  static const struct upb_refcounted_vtbl vtbl = {visitfield, freefield};
-  upb_fielddef *f = malloc(sizeof(*f));
+  upb_fielddef *f = upb_gmalloc(sizeof(*f));
   if (!f) return NULL;
-  if (!upb_def_init(upb_fielddef_upcast_mutable(f), UPB_DEF_FIELD, &vtbl, o)) {
-    free(f);
+  if (!upb_def_init(upb_fielddef_upcast_mutable(f), UPB_DEF_FIELD,
+                    &upb_fielddef_vtbl, o)) {
+    upb_gfree(f);
     return NULL;
   }
   f->msg.def = NULL;
@@ -657,7 +783,7 @@
     srcname = f->sub.def ? upb_def_fullname(f->sub.def) : NULL;
   }
   if (srcname) {
-    char *newname = malloc(strlen(f->sub.def->fullname) + 2);
+    char *newname = upb_gmalloc(strlen(f->sub.def->fullname) + 2);
     if (!newname) {
       upb_fielddef_unref(newf, owner);
       return NULL;
@@ -665,7 +791,7 @@
     strcpy(newname, ".");
     strcat(newname, f->sub.def->fullname);
     upb_fielddef_setsubdefname(newf, newname, NULL);
-    free(newname);
+    upb_gfree(newname);
   }
 
   return newf;
@@ -676,7 +802,7 @@
 }
 
 upb_fieldtype_t upb_fielddef_type(const upb_fielddef *f) {
-  assert(f->type_is_set_);
+  UPB_ASSERT(f->type_is_set_);
   return f->type_;
 }
 
@@ -716,6 +842,45 @@
   return upb_def_fullname(upb_fielddef_upcast(f));
 }
 
+size_t upb_fielddef_getjsonname(const upb_fielddef *f, char *buf, size_t len) {
+  const char *name = upb_fielddef_name(f);
+  size_t src, dst = 0;
+  bool ucase_next = false;
+
+#define WRITE(byte) \
+  ++dst; \
+  if (dst < len) buf[dst - 1] = byte; \
+  else if (dst == len) buf[dst - 1] = '\0'
+
+  if (!name) {
+    WRITE('\0');
+    return 0;
+  }
+
+  /* Implement the transformation as described in the spec:
+   *   1. upper case all letters after an underscore.
+   *   2. remove all underscores.
+   */
+  for (src = 0; name[src]; src++) {
+    if (name[src] == '_') {
+      ucase_next = true;
+      continue;
+    }
+
+    if (ucase_next) {
+      WRITE(toupper(name[src]));
+      ucase_next = false;
+    } else {
+      WRITE(name[src]);
+    }
+  }
+
+  WRITE('\0');
+  return dst;
+
+#undef WRITE
+}
+
 const upb_msgdef *upb_fielddef_containingtype(const upb_fielddef *f) {
   return f->msg_is_symbolic ? NULL : f->msg.def;
 }
@@ -733,20 +898,28 @@
 }
 
 static void release_containingtype(upb_fielddef *f) {
-  if (f->msg_is_symbolic) free(f->msg.name);
+  if (f->msg_is_symbolic) upb_gfree(f->msg.name);
 }
 
 bool upb_fielddef_setcontainingtypename(upb_fielddef *f, const char *name,
                                         upb_status *s) {
-  assert(!upb_fielddef_isfrozen(f));
+  char *name_copy;
+  UPB_ASSERT(!upb_fielddef_isfrozen(f));
   if (upb_fielddef_containingtype(f)) {
     upb_status_seterrmsg(s, "field has already been added to a message.");
     return false;
   }
   /* TODO: validate name (upb_isident() doesn't quite work atm because this name
    * may have a leading "."). */
+
+  name_copy = upb_gstrdup(name);
+  if (!name_copy) {
+    upb_upberr_setoom(s);
+    return false;
+  }
+
   release_containingtype(f);
-  f->msg.name = upb_strdup(name);
+  f->msg.name = name_copy;
   f->msg_is_symbolic = true;
   return true;
 }
@@ -762,7 +935,7 @@
 static void chkdefaulttype(const upb_fielddef *f, upb_fieldtype_t type) {
   UPB_UNUSED(f);
   UPB_UNUSED(type);
-  assert(f->type_is_set_ && upb_fielddef_type(f) == type);
+  UPB_ASSERT(f->type_is_set_ && upb_fielddef_type(f) == type);
 }
 
 int64_t upb_fielddef_defaultint64(const upb_fielddef *f) {
@@ -774,7 +947,7 @@
   if (f->type_is_set_ && upb_fielddef_type(f) == UPB_TYPE_ENUM) {
     int32_t val;
     bool ok = enumdefaultint32(f, &val);
-    UPB_ASSERT_VAR(ok, ok);
+    UPB_ASSERT(ok);
     return val;
   } else {
     chkdefaulttype(f, UPB_TYPE_INT32);
@@ -808,14 +981,14 @@
 }
 
 const char *upb_fielddef_defaultstr(const upb_fielddef *f, size_t *len) {
-  assert(f->type_is_set_);
-  assert(upb_fielddef_type(f) == UPB_TYPE_STRING ||
+  UPB_ASSERT(f->type_is_set_);
+  UPB_ASSERT(upb_fielddef_type(f) == UPB_TYPE_STRING ||
          upb_fielddef_type(f) == UPB_TYPE_BYTES ||
          upb_fielddef_type(f) == UPB_TYPE_ENUM);
 
   if (upb_fielddef_type(f) == UPB_TYPE_ENUM) {
     const char *ret = enumdefaultstr(f);
-    assert(ret);
+    UPB_ASSERT(ret);
     /* Enum defaults can't have embedded NULLs. */
     if (len) *len = strlen(ret);
     return ret;
@@ -897,8 +1070,8 @@
 }
 
 void upb_fielddef_settype(upb_fielddef *f, upb_fieldtype_t type) {
-  assert(!upb_fielddef_isfrozen(f));
-  assert(upb_fielddef_checktype(type));
+  UPB_ASSERT(!upb_fielddef_isfrozen(f));
+  UPB_ASSERT(upb_fielddef_checktype(type));
   upb_fielddef_uninit_default(f);
   f->type_ = type;
   f->type_is_set_ = true;
@@ -906,7 +1079,7 @@
 }
 
 void upb_fielddef_setdescriptortype(upb_fielddef *f, int type) {
-  assert(!upb_fielddef_isfrozen(f));
+  UPB_ASSERT(!upb_fielddef_isfrozen(f));
   switch (type) {
     case UPB_DESCRIPTOR_TYPE_DOUBLE:
       upb_fielddef_settype(f, UPB_TYPE_DOUBLE);
@@ -948,7 +1121,7 @@
     case UPB_DESCRIPTOR_TYPE_ENUM:
       upb_fielddef_settype(f, UPB_TYPE_ENUM);
       break;
-    default: assert(false);
+    default: UPB_ASSERT(false);
   }
 
   if (type == UPB_DESCRIPTOR_TYPE_FIXED64 ||
@@ -1006,34 +1179,34 @@
 }
 
 void upb_fielddef_setisextension(upb_fielddef *f, bool is_extension) {
-  assert(!upb_fielddef_isfrozen(f));
+  UPB_ASSERT(!upb_fielddef_isfrozen(f));
   f->is_extension_ = is_extension;
 }
 
 void upb_fielddef_setlazy(upb_fielddef *f, bool lazy) {
-  assert(!upb_fielddef_isfrozen(f));
+  UPB_ASSERT(!upb_fielddef_isfrozen(f));
   f->lazy_ = lazy;
 }
 
 void upb_fielddef_setpacked(upb_fielddef *f, bool packed) {
-  assert(!upb_fielddef_isfrozen(f));
+  UPB_ASSERT(!upb_fielddef_isfrozen(f));
   f->packed_ = packed;
 }
 
 void upb_fielddef_setlabel(upb_fielddef *f, upb_label_t label) {
-  assert(!upb_fielddef_isfrozen(f));
-  assert(upb_fielddef_checklabel(label));
+  UPB_ASSERT(!upb_fielddef_isfrozen(f));
+  UPB_ASSERT(upb_fielddef_checklabel(label));
   f->label_ = label;
 }
 
 void upb_fielddef_setintfmt(upb_fielddef *f, upb_intfmt_t fmt) {
-  assert(!upb_fielddef_isfrozen(f));
-  assert(upb_fielddef_checkintfmt(fmt));
+  UPB_ASSERT(!upb_fielddef_isfrozen(f));
+  UPB_ASSERT(upb_fielddef_checkintfmt(fmt));
   f->intfmt = fmt;
 }
 
 void upb_fielddef_settagdelim(upb_fielddef *f, bool tag_delim) {
-  assert(!upb_fielddef_isfrozen(f));
+  UPB_ASSERT(!upb_fielddef_isfrozen(f));
   f->tagdelim = tag_delim;
   f->tagdelim = tag_delim;
 }
@@ -1041,12 +1214,12 @@
 static bool checksetdefault(upb_fielddef *f, upb_fieldtype_t type) {
   if (!f->type_is_set_ || upb_fielddef_isfrozen(f) ||
       upb_fielddef_type(f) != type) {
-    assert(false);
+    UPB_ASSERT(false);
     return false;
   }
   if (f->default_is_string) {
     str_t *s = f->defaultval.bytes;
-    assert(s || type == UPB_TYPE_ENUM);
+    UPB_ASSERT(s || type == UPB_TYPE_ENUM);
     if (s) freestr(s);
   }
   f->default_is_string = false;
@@ -1094,16 +1267,16 @@
 bool upb_fielddef_setdefaultstr(upb_fielddef *f, const void *str, size_t len,
                                 upb_status *s) {
   str_t *str2;
-  assert(upb_fielddef_isstring(f) || f->type_ == UPB_TYPE_ENUM);
+  UPB_ASSERT(upb_fielddef_isstring(f) || f->type_ == UPB_TYPE_ENUM);
   if (f->type_ == UPB_TYPE_ENUM && !upb_isident(str, len, false, s))
     return false;
 
   if (f->default_is_string) {
     str_t *s = f->defaultval.bytes;
-    assert(s || f->type_ == UPB_TYPE_ENUM);
+    UPB_ASSERT(s || f->type_ == UPB_TYPE_ENUM);
     if (s) freestr(s);
   } else {
-    assert(f->type_ == UPB_TYPE_ENUM);
+    UPB_ASSERT(f->type_ == UPB_TYPE_ENUM);
   }
 
   str2 = newstr(str, len);
@@ -1114,18 +1287,18 @@
 
 void upb_fielddef_setdefaultcstr(upb_fielddef *f, const char *str,
                                  upb_status *s) {
-  assert(f->type_is_set_);
+  UPB_ASSERT(f->type_is_set_);
   upb_fielddef_setdefaultstr(f, str, str ? strlen(str) : 0, s);
 }
 
 bool upb_fielddef_enumhasdefaultint32(const upb_fielddef *f) {
   int32_t val;
-  assert(f->type_is_set_ && f->type_ == UPB_TYPE_ENUM);
+  UPB_ASSERT(f->type_is_set_ && f->type_ == UPB_TYPE_ENUM);
   return enumdefaultint32(f, &val);
 }
 
 bool upb_fielddef_enumhasdefaultstr(const upb_fielddef *f) {
-  assert(f->type_is_set_ && f->type_ == UPB_TYPE_ENUM);
+  UPB_ASSERT(f->type_is_set_ && f->type_ == UPB_TYPE_ENUM);
   return enumdefaultstr(f) != NULL;
 }
 
@@ -1147,7 +1320,7 @@
 
 static void release_subdef(upb_fielddef *f) {
   if (f->subdef_is_symbolic) {
-    free(f->sub.name);
+    upb_gfree(f->sub.name);
   } else if (f->sub.def) {
     upb_unref2(f->sub.def, f);
   }
@@ -1155,8 +1328,8 @@
 
 bool upb_fielddef_setsubdef(upb_fielddef *f, const upb_def *subdef,
                             upb_status *s) {
-  assert(!upb_fielddef_isfrozen(f));
-  assert(upb_fielddef_hassubdef(f));
+  UPB_ASSERT(!upb_fielddef_isfrozen(f));
+  UPB_ASSERT(upb_fielddef_hassubdef(f));
   if (subdef && !upb_subdef_typecheck(f, subdef, s)) return false;
   release_subdef(f);
   f->sub.def = subdef;
@@ -1177,15 +1350,23 @@
 
 bool upb_fielddef_setsubdefname(upb_fielddef *f, const char *name,
                                 upb_status *s) {
-  assert(!upb_fielddef_isfrozen(f));
+  char *name_copy;
+  UPB_ASSERT(!upb_fielddef_isfrozen(f));
   if (!upb_fielddef_hassubdef(f)) {
     upb_status_seterrmsg(s, "field type does not accept a subdef");
     return false;
   }
+
+  name_copy = upb_gstrdup(name);
+  if (!name_copy) {
+    upb_upberr_setoom(s);
+    return false;
+  }
+
   /* TODO: validate name (upb_isident() doesn't quite work atm because this name
    * may have a leading "."). */
   release_subdef(f);
-  f->sub.name = upb_strdup(name);
+  f->sub.name = name_copy;
   f->subdef_is_symbolic = true;
   return true;
 }
@@ -1212,6 +1393,16 @@
          upb_msgdef_mapentry(upb_fielddef_msgsubdef(f));
 }
 
+bool upb_fielddef_haspresence(const upb_fielddef *f) {
+  if (upb_fielddef_isseq(f)) return false;
+  if (upb_fielddef_issubmsg(f)) return true;
+
+  /* Primitive field: return true unless there is a message that specifies
+   * presence should not exist. */
+  if (f->msg_is_symbolic || !f->msg.def) return true;
+  return f->msg.def->syntax == UPB_SYNTAX_PROTO2;
+}
+
 bool upb_fielddef_hassubdef(const upb_fielddef *f) {
   return upb_fielddef_issubmsg(f) || upb_fielddef_type(f) == UPB_TYPE_ENUM;
 }
@@ -1234,6 +1425,7 @@
                      void *closure) {
   upb_msg_oneof_iter o;
   const upb_msgdef *m = (const upb_msgdef*)r;
+  const upb_def *def = upb_msgdef_upcast(m);
   upb_msg_field_iter i;
   for(upb_msg_field_begin(&i, m);
       !upb_msg_field_done(&i);
@@ -1245,37 +1437,42 @@
       !upb_msg_oneof_done(&o);
       upb_msg_oneof_next(&o)) {
     upb_oneofdef *f = upb_msg_iter_oneof(&o);
-    visit(r, upb_oneofdef_upcast2(f), closure);
+    visit(r, upb_oneofdef_upcast(f), closure);
+  }
+  if (upb_def_file(def)) {
+    visit(r, upb_filedef_upcast(upb_def_file(def)), closure);
   }
 }
 
 static void freemsg(upb_refcounted *r) {
   upb_msgdef *m = (upb_msgdef*)r;
-  upb_strtable_uninit(&m->ntoo);
   upb_strtable_uninit(&m->ntof);
   upb_inttable_uninit(&m->itof);
   upb_def_uninit(upb_msgdef_upcast_mutable(m));
-  free(m);
+  upb_gfree(m);
 }
 
+const struct upb_refcounted_vtbl upb_msgdef_vtbl = {visitmsg, freemsg};
+
 upb_msgdef *upb_msgdef_new(const void *owner) {
-  static const struct upb_refcounted_vtbl vtbl = {visitmsg, freemsg};
-  upb_msgdef *m = malloc(sizeof(*m));
+  upb_msgdef *m = upb_gmalloc(sizeof(*m));
   if (!m) return NULL;
-  if (!upb_def_init(upb_msgdef_upcast_mutable(m), UPB_DEF_MSG, &vtbl, owner))
+
+  if (!upb_def_init(upb_msgdef_upcast_mutable(m), UPB_DEF_MSG, &upb_msgdef_vtbl,
+                    owner)) {
     goto err2;
-  if (!upb_inttable_init(&m->itof, UPB_CTYPE_PTR)) goto err3;
-  if (!upb_strtable_init(&m->ntof, UPB_CTYPE_PTR)) goto err2;
-  if (!upb_strtable_init(&m->ntoo, UPB_CTYPE_PTR)) goto err1;
+  }
+
+  if (!upb_inttable_init(&m->itof, UPB_CTYPE_PTR)) goto err2;
+  if (!upb_strtable_init(&m->ntof, UPB_CTYPE_PTR)) goto err1;
   m->map_entry = false;
+  m->syntax = UPB_SYNTAX_PROTO2;
   return m;
 
 err1:
-  upb_strtable_uninit(&m->ntof);
-err2:
   upb_inttable_uninit(&m->itof);
-err3:
-  free(m);
+err2:
+  upb_gfree(m);
   return NULL;
 }
 
@@ -1290,7 +1487,8 @@
                            upb_def_fullname(upb_msgdef_upcast(m)),
                            NULL);
   newm->map_entry = m->map_entry;
-  UPB_ASSERT_VAR(ok, ok);
+  newm->syntax = m->syntax;
+  UPB_ASSERT(ok);
   for(upb_msg_field_begin(&i, m);
       !upb_msg_field_done(&i);
       upb_msg_field_next(&i)) {
@@ -1323,11 +1521,28 @@
   return upb_def_fullname(upb_msgdef_upcast(m));
 }
 
+const char *upb_msgdef_name(const upb_msgdef *m) {
+  return upb_def_name(upb_msgdef_upcast(m));
+}
+
 bool upb_msgdef_setfullname(upb_msgdef *m, const char *fullname,
                             upb_status *s) {
   return upb_def_setfullname(upb_msgdef_upcast_mutable(m), fullname, s);
 }
 
+bool upb_msgdef_setsyntax(upb_msgdef *m, upb_syntax_t syntax) {
+  if (syntax != UPB_SYNTAX_PROTO2 && syntax != UPB_SYNTAX_PROTO3) {
+    return false;
+  }
+
+  m->syntax = syntax;
+  return true;
+}
+
+upb_syntax_t upb_msgdef_syntax(const upb_msgdef *m) {
+  return m->syntax;
+}
+
 /* Helper: check that the field |f| is safe to add to msgdef |m|. Set an error
  * on status |s| and return false if not. */
 static bool check_field_add(const upb_msgdef *m, const upb_fielddef *f,
@@ -1338,9 +1553,11 @@
   } else if (upb_fielddef_name(f) == NULL || upb_fielddef_number(f) == 0) {
     upb_status_seterrmsg(s, "field name or number were not set");
     return false;
-  } else if (upb_msgdef_ntofz(m, upb_fielddef_name(f)) ||
-             upb_msgdef_itof(m, upb_fielddef_number(f))) {
-    upb_status_seterrmsg(s, "duplicate field name or number for field");
+  } else if (upb_msgdef_itof(m, upb_fielddef_number(f))) {
+    upb_status_seterrmsg(s, "duplicate field number");
+    return false;
+  } else if (upb_strtable_lookup(&m->ntof, upb_fielddef_name(f), NULL)) {
+    upb_status_seterrmsg(s, "name conflicts with existing field or oneof");
     return false;
   }
   return true;
@@ -1373,6 +1590,7 @@
    * This method is idempotent. Check if |f| is already part of this msgdef and
    * return immediately if so. */
   if (upb_fielddef_containingtype(f) == m) {
+    if (ref_donor) upb_fielddef_unref(f, ref_donor);
     return true;
   }
 
@@ -1401,8 +1619,8 @@
   } else if (upb_oneofdef_name(o) == NULL) {
     upb_status_seterrmsg(s, "oneofdef name was not set");
     return false;
-  } else if (upb_msgdef_ntooz(m, upb_oneofdef_name(o))) {
-    upb_status_seterrmsg(s, "duplicate oneof name");
+  } else if (upb_strtable_lookup(&m->ntof, upb_oneofdef_name(o), NULL)) {
+    upb_status_seterrmsg(s, "name conflicts with existing field or oneof");
     return false;
   }
 
@@ -1419,7 +1637,7 @@
 
   /* Add oneof itself first. */
   o->parent = m;
-  upb_strtable_insert(&m->ntoo, upb_oneofdef_name(o), upb_value_ptr(o));
+  upb_strtable_insert(&m->ntof, upb_oneofdef_name(o), upb_value_ptr(o));
   upb_ref2(o, m);
   upb_ref2(m, o);
 
@@ -1443,27 +1661,51 @@
 const upb_fielddef *upb_msgdef_ntof(const upb_msgdef *m, const char *name,
                                     size_t len) {
   upb_value val;
-  return upb_strtable_lookup2(&m->ntof, name, len, &val) ?
-      upb_value_getptr(val) : NULL;
+
+  if (!upb_strtable_lookup2(&m->ntof, name, len, &val)) {
+    return NULL;
+  }
+
+  return upb_trygetfield(upb_value_getptr(val));
 }
 
 const upb_oneofdef *upb_msgdef_ntoo(const upb_msgdef *m, const char *name,
                                     size_t len) {
   upb_value val;
-  return upb_strtable_lookup2(&m->ntoo, name, len, &val) ?
-      upb_value_getptr(val) : NULL;
+
+  if (!upb_strtable_lookup2(&m->ntof, name, len, &val)) {
+    return NULL;
+  }
+
+  return upb_trygetoneof(upb_value_getptr(val));
+}
+
+bool upb_msgdef_lookupname(const upb_msgdef *m, const char *name, size_t len,
+                           const upb_fielddef **f, const upb_oneofdef **o) {
+  upb_value val;
+
+  if (!upb_strtable_lookup2(&m->ntof, name, len, &val)) {
+    return false;
+  }
+
+  *o = upb_trygetoneof(upb_value_getptr(val));
+  *f = upb_trygetfield(upb_value_getptr(val));
+  UPB_ASSERT((*o != NULL) ^ (*f != NULL));  /* Exactly one of the two should be set. */
+  return true;
 }
 
 int upb_msgdef_numfields(const upb_msgdef *m) {
-  return upb_strtable_count(&m->ntof);
+  /* The number table contains only fields. */
+  return upb_inttable_count(&m->itof);
 }
 
 int upb_msgdef_numoneofs(const upb_msgdef *m) {
-  return upb_strtable_count(&m->ntoo);
+  /* The name table includes oneofs, and the number table does not. */
+  return upb_strtable_count(&m->ntof) - upb_inttable_count(&m->itof);
 }
 
 void upb_msgdef_setmapentry(upb_msgdef *m, bool map_entry) {
-  assert(!upb_msgdef_isfrozen(m));
+  UPB_ASSERT(!upb_msgdef_isfrozen(m));
   m->map_entry = map_entry;
 }
 
@@ -1490,10 +1732,21 @@
 }
 
 void upb_msg_oneof_begin(upb_msg_oneof_iter *iter, const upb_msgdef *m) {
-  upb_strtable_begin(iter, &m->ntoo);
+  upb_strtable_begin(iter, &m->ntof);
+  /* We need to skip past any initial fields. */
+  while (!upb_strtable_done(iter) &&
+         !upb_isoneof(upb_value_getptr(upb_strtable_iter_value(iter)))) {
+    upb_strtable_next(iter);
+  }
 }
 
-void upb_msg_oneof_next(upb_msg_oneof_iter *iter) { upb_strtable_next(iter); }
+void upb_msg_oneof_next(upb_msg_oneof_iter *iter) {
+  /* We need to skip past fields to return only oneofs. */
+  do {
+    upb_strtable_next(iter);
+  } while (!upb_strtable_done(iter) &&
+           !upb_isoneof(upb_value_getptr(upb_strtable_iter_value(iter))));
+}
 
 bool upb_msg_oneof_done(const upb_msg_oneof_iter *iter) {
   return upb_strtable_done(iter);
@@ -1526,26 +1779,36 @@
   upb_oneofdef *o = (upb_oneofdef*)r;
   upb_strtable_uninit(&o->ntof);
   upb_inttable_uninit(&o->itof);
-  upb_def_uninit(upb_oneofdef_upcast_mutable(o));
-  free(o);
+  upb_gfree((void*)o->name);
+  upb_gfree(o);
 }
 
+const struct upb_refcounted_vtbl upb_oneofdef_vtbl = {visitoneof, freeoneof};
+
 upb_oneofdef *upb_oneofdef_new(const void *owner) {
-  static const struct upb_refcounted_vtbl vtbl = {visitoneof, freeoneof};
-  upb_oneofdef *o = malloc(sizeof(*o));
+  upb_oneofdef *o = upb_gmalloc(sizeof(*o));
+
+  if (!o) {
+    return NULL;
+  }
+
   o->parent = NULL;
-  if (!o) return NULL;
-  if (!upb_def_init(upb_oneofdef_upcast_mutable(o), UPB_DEF_ONEOF, &vtbl,
-                    owner))
+  o->name = NULL;
+
+  if (!upb_refcounted_init(upb_oneofdef_upcast_mutable(o), &upb_oneofdef_vtbl,
+                           owner)) {
     goto err2;
+  }
+
   if (!upb_inttable_init(&o->itof, UPB_CTYPE_PTR)) goto err2;
   if (!upb_strtable_init(&o->ntof, UPB_CTYPE_PTR)) goto err1;
+
   return o;
 
 err1:
   upb_inttable_uninit(&o->itof);
 err2:
-  free(o);
+  upb_gfree(o);
   return NULL;
 }
 
@@ -1554,9 +1817,8 @@
   upb_oneof_iter i;
   upb_oneofdef *newo = upb_oneofdef_new(owner);
   if (!newo) return NULL;
-  ok = upb_def_setfullname(upb_oneofdef_upcast_mutable(newo),
-                           upb_def_fullname(upb_oneofdef_upcast(o)), NULL);
-  UPB_ASSERT_VAR(ok, ok);
+  ok = upb_oneofdef_setname(newo, upb_oneofdef_name(o), NULL);
+  UPB_ASSERT(ok);
   for (upb_oneof_begin(&i, o); !upb_oneof_done(&i); upb_oneof_next(&i)) {
     upb_fielddef *f = upb_fielddef_dup(upb_oneof_iter_field(&i), &f);
     if (!f || !upb_oneofdef_addfield(newo, f, &f, NULL)) {
@@ -1567,17 +1829,28 @@
   return newo;
 }
 
-const char *upb_oneofdef_name(const upb_oneofdef *o) {
-  return upb_def_fullname(upb_oneofdef_upcast(o));
-}
+const char *upb_oneofdef_name(const upb_oneofdef *o) { return o->name; }
 
-bool upb_oneofdef_setname(upb_oneofdef *o, const char *fullname,
-                             upb_status *s) {
+bool upb_oneofdef_setname(upb_oneofdef *o, const char *name, upb_status *s) {
+  UPB_ASSERT(!upb_oneofdef_isfrozen(o));
   if (upb_oneofdef_containingtype(o)) {
     upb_status_seterrmsg(s, "oneof already added to a message");
     return false;
   }
-  return upb_def_setfullname(upb_oneofdef_upcast_mutable(o), fullname, s);
+
+  if (!upb_isident(name, strlen(name), true, s)) {
+    return false;
+  }
+
+  name = upb_gstrdup(name);
+  if (!name) {
+    upb_status_seterrmsg(s, "One of memory");
+    return false;
+  }
+
+  upb_gfree((void*)o->name);
+  o->name = name;
+  return true;
 }
 
 const upb_msgdef *upb_oneofdef_containingtype(const upb_oneofdef *o) {
@@ -1591,8 +1864,8 @@
 bool upb_oneofdef_addfield(upb_oneofdef *o, upb_fielddef *f,
                            const void *ref_donor,
                            upb_status *s) {
-  assert(!upb_oneofdef_isfrozen(o));
-  assert(!o->parent || !upb_msgdef_isfrozen(o->parent));
+  UPB_ASSERT(!upb_oneofdef_isfrozen(o));
+  UPB_ASSERT(!o->parent || !upb_msgdef_isfrozen(o->parent));
 
   /* This method is idempotent. Check if |f| is already part of this oneofdef
    * and return immediately if so. */
@@ -1696,287 +1969,209 @@
   upb_inttable_iter_setdone(iter);
 }
 
+/* upb_filedef ****************************************************************/
 
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
+static void visitfiledef(const upb_refcounted *r, upb_refcounted_visit *visit,
+                         void *closure) {
+  const upb_filedef *f = (const upb_filedef*)r;
+  size_t i;
 
-typedef struct cleanup_ent {
-  upb_cleanup_func *cleanup;
-  void *ud;
-  struct cleanup_ent *next;
-} cleanup_ent;
-
-static void *seeded_alloc(void *ud, void *ptr, size_t oldsize, size_t size);
-
-/* Default allocator **********************************************************/
-
-/* Just use realloc, keeping all allocated blocks in a linked list to destroy at
- * the end. */
-
-typedef struct mem_block {
-  /* List is doubly-linked, because in cases where realloc() moves an existing
-   * block, we need to be able to remove the old pointer from the list
-   * efficiently. */
-  struct mem_block *prev, *next;
-#ifndef NDEBUG
-  size_t size;  /* Doesn't include mem_block structure. */
-#endif
-} mem_block;
-
-typedef struct {
-  mem_block *head;
-} default_alloc_ud;
-
-static void *default_alloc(void *_ud, void *ptr, size_t oldsize, size_t size) {
-  default_alloc_ud *ud = _ud;
-  mem_block *from, *block;
-  void *ret;
-  UPB_UNUSED(oldsize);
-
-  from = ptr ? (void*)((char*)ptr - sizeof(mem_block)) : NULL;
-
-#ifndef NDEBUG
-  if (from) {
-    assert(oldsize <= from->size);
+  for(i = 0; i < upb_filedef_defcount(f); i++) {
+    visit(r, upb_def_upcast(upb_filedef_def(f, i)), closure);
   }
-#endif
+}
 
-  /* TODO(haberman): we probably need to provide even better alignment here,
-   * like 16-byte alignment of the returned data pointer. */
-  block = realloc(from, size + sizeof(mem_block));
-  if (!block) return NULL;
-  ret = (char*)block + sizeof(*block);
+static void freefiledef(upb_refcounted *r) {
+  upb_filedef *f = (upb_filedef*)r;
+  size_t i;
 
-#ifndef NDEBUG
-  block->size = size;
-#endif
+  for(i = 0; i < upb_filedef_depcount(f); i++) {
+    upb_filedef_unref(upb_filedef_dep(f, i), f);
+  }
 
-  if (from) {
-    if (block != from) {
-      /* The block was moved, so pointers in next and prev blocks must be
-       * updated to its new location. */
-      if (block->next) block->next->prev = block;
-      if (block->prev) block->prev->next = block;
-      if (ud->head == from) ud->head = block;
-    }
+  upb_inttable_uninit(&f->defs);
+  upb_inttable_uninit(&f->deps);
+  upb_gfree((void*)f->name);
+  upb_gfree((void*)f->package);
+  upb_gfree(f);
+}
+
+const struct upb_refcounted_vtbl upb_filedef_vtbl = {visitfiledef, freefiledef};
+
+upb_filedef *upb_filedef_new(const void *owner) {
+  upb_filedef *f = upb_gmalloc(sizeof(*f));
+
+  if (!f) {
+    return NULL;
+  }
+
+  f->package = NULL;
+  f->name = NULL;
+  f->syntax = UPB_SYNTAX_PROTO2;
+
+  if (!upb_refcounted_init(upb_filedef_upcast_mutable(f), &upb_filedef_vtbl,
+                           owner)) {
+    goto err;
+  }
+
+  if (!upb_inttable_init(&f->defs, UPB_CTYPE_CONSTPTR)) {
+    goto err;
+  }
+
+  if (!upb_inttable_init(&f->deps, UPB_CTYPE_CONSTPTR)) {
+    goto err2;
+  }
+
+  return f;
+
+
+err2:
+  upb_inttable_uninit(&f->defs);
+
+err:
+  upb_gfree(f);
+  return NULL;
+}
+
+const char *upb_filedef_name(const upb_filedef *f) {
+  return f->name;
+}
+
+const char *upb_filedef_package(const upb_filedef *f) {
+  return f->package;
+}
+
+upb_syntax_t upb_filedef_syntax(const upb_filedef *f) {
+  return f->syntax;
+}
+
+size_t upb_filedef_defcount(const upb_filedef *f) {
+  return upb_inttable_count(&f->defs);
+}
+
+size_t upb_filedef_depcount(const upb_filedef *f) {
+  return upb_inttable_count(&f->deps);
+}
+
+const upb_def *upb_filedef_def(const upb_filedef *f, size_t i) {
+  upb_value v;
+
+  if (upb_inttable_lookup32(&f->defs, i, &v)) {
+    return upb_value_getconstptr(v);
   } else {
-    /* Insert at head of linked list. */
-    block->prev = NULL;
-    block->next = ud->head;
-    if (block->next) block->next->prev = block;
-    ud->head = block;
-  }
-
-  return ret;
-}
-
-static void default_alloc_cleanup(void *_ud) {
-  default_alloc_ud *ud = _ud;
-  mem_block *block = ud->head;
-
-  while (block) {
-    void *to_free = block;
-    block = block->next;
-    free(to_free);
+    return NULL;
   }
 }
 
+const upb_filedef *upb_filedef_dep(const upb_filedef *f, size_t i) {
+  upb_value v;
 
-/* Standard error functions ***************************************************/
-
-static bool default_err(void *ud, const upb_status *status) {
-  UPB_UNUSED(ud);
-  UPB_UNUSED(status);
-  return false;
-}
-
-static bool write_err_to(void *ud, const upb_status *status) {
-  upb_status *copy_to = ud;
-  upb_status_copy(copy_to, status);
-  return false;
-}
-
-
-/* upb_env ********************************************************************/
-
-void upb_env_init(upb_env *e) {
-  default_alloc_ud *ud = (default_alloc_ud*)&e->default_alloc_ud;
-  e->ok_ = true;
-  e->bytes_allocated = 0;
-  e->cleanup_head = NULL;
-
-  ud->head = NULL;
-
-  /* Set default functions. */
-  upb_env_setallocfunc(e, default_alloc, ud);
-  upb_env_seterrorfunc(e, default_err, NULL);
-}
-
-void upb_env_uninit(upb_env *e) {
-  cleanup_ent *ent = e->cleanup_head;
-
-  while (ent) {
-    ent->cleanup(ent->ud);
-    ent = ent->next;
-  }
-
-  /* Must do this after running cleanup functions, because this will delete
-     the memory we store our cleanup entries in! */
-  if (e->alloc == default_alloc) {
-    default_alloc_cleanup(e->alloc_ud);
+  if (upb_inttable_lookup32(&f->deps, i, &v)) {
+    return upb_value_getconstptr(v);
+  } else {
+    return NULL;
   }
 }
 
-UPB_FORCEINLINE void upb_env_setallocfunc(upb_env *e, upb_alloc_func *alloc,
-                                          void *ud) {
-  e->alloc = alloc;
-  e->alloc_ud = ud;
+bool upb_filedef_setname(upb_filedef *f, const char *name, upb_status *s) {
+  name = upb_gstrdup(name);
+  if (!name) {
+    upb_upberr_setoom(s);
+    return false;
+  }
+  upb_gfree((void*)f->name);
+  f->name = name;
+  return true;
 }
 
-UPB_FORCEINLINE void upb_env_seterrorfunc(upb_env *e, upb_error_func *func,
-                                          void *ud) {
-  e->err = func;
-  e->err_ud = ud;
+bool upb_filedef_setpackage(upb_filedef *f, const char *package,
+                            upb_status *s) {
+  if (!upb_isident(package, strlen(package), true, s)) return false;
+  package = upb_gstrdup(package);
+  if (!package) {
+    upb_upberr_setoom(s);
+    return false;
+  }
+  upb_gfree((void*)f->package);
+  f->package = package;
+  return true;
 }
 
-void upb_env_reporterrorsto(upb_env *e, upb_status *status) {
-  e->err = write_err_to;
-  e->err_ud = status;
-}
+bool upb_filedef_setsyntax(upb_filedef *f, upb_syntax_t syntax,
+                           upb_status *s) {
+  UPB_UNUSED(s);
+  if (syntax != UPB_SYNTAX_PROTO2 &&
+      syntax != UPB_SYNTAX_PROTO3) {
+    upb_status_seterrmsg(s, "Unknown syntax value.");
+    return false;
+  }
+  f->syntax = syntax;
 
-bool upb_env_ok(const upb_env *e) {
-  return e->ok_;
-}
+  {
+    /* Set all messages in this file to match. */
+    size_t i;
+    for (i = 0; i < upb_filedef_defcount(f); i++) {
+      /* Casting const away is safe since all defs in mutable filedef must
+       * also be mutable. */
+      upb_def *def = (upb_def*)upb_filedef_def(f, i);
 
-bool upb_env_reporterror(upb_env *e, const upb_status *status) {
-  e->ok_ = false;
-  return e->err(e->err_ud, status);
-}
-
-bool upb_env_addcleanup(upb_env *e, upb_cleanup_func *func, void *ud) {
-  cleanup_ent *ent = upb_env_malloc(e, sizeof(cleanup_ent));
-  if (!ent) return false;
-
-  ent->cleanup = func;
-  ent->ud = ud;
-  ent->next = e->cleanup_head;
-  e->cleanup_head = ent;
+      upb_msgdef *m = upb_dyncast_msgdef_mutable(def);
+      if (m) {
+        m->syntax = syntax;
+      }
+    }
+  }
 
   return true;
 }
 
-void *upb_env_malloc(upb_env *e, size_t size) {
-  e->bytes_allocated += size;
-  if (e->alloc == seeded_alloc) {
-    /* This is equivalent to the next branch, but allows inlining for a
-     * measurable perf benefit. */
-    return seeded_alloc(e->alloc_ud, NULL, 0, size);
-  } else {
-    return e->alloc(e->alloc_ud, NULL, 0, size);
+bool upb_filedef_adddef(upb_filedef *f, upb_def *def, const void *ref_donor,
+                        upb_status *s) {
+  if (def->file) {
+    upb_status_seterrmsg(s, "Def is already part of another filedef.");
+    return false;
   }
-}
 
-void *upb_env_realloc(upb_env *e, void *ptr, size_t oldsize, size_t size) {
-  char *ret;
-  assert(oldsize <= size);
-  ret = e->alloc(e->alloc_ud, ptr, oldsize, size);
-
-#ifndef NDEBUG
-  /* Overwrite non-preserved memory to ensure callers are passing the oldsize
-   * that they truly require. */
-  memset(ret + oldsize, 0xff, size - oldsize);
-#endif
-
-  return ret;
-}
-
-size_t upb_env_bytesallocated(const upb_env *e) {
-  return e->bytes_allocated;
-}
-
-
-/* upb_seededalloc ************************************************************/
-
-/* Be conservative and choose 16 in case anyone is using SSE. */
-static const size_t maxalign = 16;
-
-static size_t align_up(size_t size) {
-  return ((size + maxalign - 1) / maxalign) * maxalign;
-}
-
-UPB_FORCEINLINE static void *seeded_alloc(void *ud, void *ptr, size_t oldsize,
-                                          size_t size) {
-  upb_seededalloc *a = ud;
-
-  size = align_up(size);
-
-  assert(a->mem_limit >= a->mem_ptr);
-
-  if (oldsize == 0 && size <= (size_t)(a->mem_limit - a->mem_ptr)) {
-    /* Fast path: we can satisfy from the initial allocation. */
-    void *ret = a->mem_ptr;
-    a->mem_ptr += size;
-    return ret;
-  } else {
-    char *chptr = ptr;
-    /* Slow path: fallback to other allocator. */
-    a->need_cleanup = true;
-    /* Is `ptr` part of the user-provided initial block? Don't pass it to the
-     * default allocator if so; otherwise, it may try to realloc() the block. */
-    if (chptr >= a->mem_base && chptr < a->mem_limit) {
-      void *ret;
-      assert(chptr + oldsize <= a->mem_limit);
-      ret = a->alloc(a->alloc_ud, NULL, 0, size);
-      if (ret) memcpy(ret, ptr, oldsize);
-      return ret;
-    } else {
-      return a->alloc(a->alloc_ud, ptr, oldsize, size);
+  if (upb_inttable_push(&f->defs, upb_value_constptr(def))) {
+    def->file = f;
+    upb_ref2(def, f);
+    upb_ref2(f, def);
+    if (ref_donor) upb_def_unref(def, ref_donor);
+    if (def->type == UPB_DEF_MSG) {
+      upb_downcast_msgdef_mutable(def)->syntax = f->syntax;
     }
+    return true;
+  } else {
+    upb_upberr_setoom(s);
+    return false;
   }
 }
 
-void upb_seededalloc_init(upb_seededalloc *a, void *mem, size_t len) {
-  default_alloc_ud *ud = (default_alloc_ud*)&a->default_alloc_ud;
-  a->mem_base = mem;
-  a->mem_ptr = mem;
-  a->mem_limit = (char*)mem + len;
-  a->need_cleanup = false;
-  a->returned_allocfunc = false;
-
-  ud->head = NULL;
-
-  upb_seededalloc_setfallbackalloc(a, default_alloc, ud);
-}
-
-void upb_seededalloc_uninit(upb_seededalloc *a) {
-  if (a->alloc == default_alloc && a->need_cleanup) {
-    default_alloc_cleanup(a->alloc_ud);
+bool upb_filedef_adddep(upb_filedef *f, const upb_filedef *dep) {
+  if (upb_inttable_push(&f->deps, upb_value_constptr(dep))) {
+    /* Regular ref instead of ref2 because files can't form cycles. */
+    upb_filedef_ref(dep, f);
+    return true;
+  } else {
+    return false;
   }
 }
-
-UPB_FORCEINLINE void upb_seededalloc_setfallbackalloc(upb_seededalloc *a,
-                                                      upb_alloc_func *alloc,
-                                                      void *ud) {
-  assert(!a->returned_allocfunc);
-  a->alloc = alloc;
-  a->alloc_ud = ud;
-}
-
-upb_alloc_func *upb_seededalloc_getallocfunc(upb_seededalloc *a) {
-  a->returned_allocfunc = true;
-  return seeded_alloc;
-}
 /*
 ** TODO(haberman): it's unclear whether a lot of the consistency checks should
-** assert() or return false.
+** UPB_ASSERT() or return false.
 */
 
 
-#include <stdlib.h>
 #include <string.h>
 
 
+static void *upb_calloc(size_t size) {
+  void *mem = upb_gmalloc(size);
+  if (mem) {
+    memset(mem, 0, size);
+  }
+  return mem;
+}
 
 /* Defined for the sole purpose of having a unique pointer value for
  * UPB_NO_CLOSURE. */
@@ -1996,8 +2191,8 @@
 
   upb_inttable_uninit(&h->cleanup_);
   upb_msgdef_unref(h->msg, h);
-  free(h->sub);
-  free(h);
+  upb_gfree(h->sub);
+  upb_gfree(h);
 }
 
 static void visithandlers(const upb_refcounted *r, upb_refcounted_visit *visit,
@@ -2073,7 +2268,7 @@
 static int32_t trygetsel(upb_handlers *h, const upb_fielddef *f,
                          upb_handlertype_t type) {
   upb_selector_t sel;
-  assert(!upb_handlers_isfrozen(h));
+  UPB_ASSERT(!upb_handlers_isfrozen(h));
   if (upb_handlers_msgdef(h) != upb_fielddef_containingtype(f)) {
     upb_status_seterrf(
         &h->status_, "type mismatch: field %s does not belong to message %s",
@@ -2093,7 +2288,7 @@
 static upb_selector_t handlers_getsel(upb_handlers *h, const upb_fielddef *f,
                              upb_handlertype_t type) {
   int32_t sel = trygetsel(h, f, type);
-  assert(sel >= 0);
+  UPB_ASSERT(sel >= 0);
   return sel;
 }
 
@@ -2109,7 +2304,7 @@
   const void *closure_type;
   const void **context_closure_type;
 
-  assert(!upb_handlers_isfrozen(h));
+  UPB_ASSERT(!upb_handlers_isfrozen(h));
 
   if (sel < 0) {
     upb_status_seterrmsg(&h->status_,
@@ -2189,7 +2384,7 @@
   const void *ret;
   upb_selector_t sel;
 
-  assert(type != UPB_HANDLER_STRING);
+  UPB_ASSERT(type != UPB_HANDLER_STRING);
   ret = h->top_closure_type;
 
   if (upb_fielddef_isseq(f) &&
@@ -2244,17 +2439,23 @@
   int extra;
   upb_handlers *h;
 
-  assert(upb_msgdef_isfrozen(md));
+  UPB_ASSERT(upb_msgdef_isfrozen(md));
 
   extra = sizeof(upb_handlers_tabent) * (md->selector_count - 1);
-  h = calloc(sizeof(*h) + extra, 1);
+  h = upb_calloc(sizeof(*h) + extra);
   if (!h) return NULL;
 
   h->msg = md;
   upb_msgdef_ref(h->msg, h);
   upb_status_clear(&h->status_);
-  h->sub = calloc(md->submsg_field_count, sizeof(*h->sub));
-  if (!h->sub) goto oom;
+
+  if (md->submsg_field_count > 0) {
+    h->sub = upb_calloc(md->submsg_field_count * sizeof(*h->sub));
+    if (!h->sub) goto oom;
+  } else {
+    h->sub = 0;
+  }
+
   if (!upb_refcounted_init(upb_handlers_upcast_mutable(h), &vtbl, owner))
     goto oom;
   if (!upb_inttable_init(&h->cleanup_, UPB_CTYPE_FPTR)) goto oom;
@@ -2287,18 +2488,18 @@
 
   r = upb_handlers_upcast_mutable(ret);
   ok = upb_refcounted_freeze(&r, 1, NULL, UPB_MAX_HANDLER_DEPTH);
-  UPB_ASSERT_VAR(ok, ok);
+  UPB_ASSERT(ok);
 
   return ret;
 }
 
 const upb_status *upb_handlers_status(upb_handlers *h) {
-  assert(!upb_handlers_isfrozen(h));
+  UPB_ASSERT(!upb_handlers_isfrozen(h));
   return &h->status_;
 }
 
 void upb_handlers_clearerr(upb_handlers *h) {
-  assert(!upb_handlers_isfrozen(h));
+  UPB_ASSERT(!upb_handlers_isfrozen(h));
   upb_status_clear(&h->status_);
 }
 
@@ -2334,16 +2535,16 @@
 
 bool upb_handlers_setendmsg(upb_handlers *h, upb_endmsg_handlerfunc *func,
                             upb_handlerattr *attr) {
-  assert(!upb_handlers_isfrozen(h));
+  UPB_ASSERT(!upb_handlers_isfrozen(h));
   return doset(h, UPB_ENDMSG_SELECTOR, NULL, UPB_HANDLER_INT32,
                (upb_func *)func, attr);
 }
 
 bool upb_handlers_setsubhandlers(upb_handlers *h, const upb_fielddef *f,
                                  const upb_handlers *sub) {
-  assert(sub);
-  assert(!upb_handlers_isfrozen(h));
-  assert(upb_fielddef_issubmsg(f));
+  UPB_ASSERT(sub);
+  UPB_ASSERT(!upb_handlers_isfrozen(h));
+  UPB_ASSERT(upb_fielddef_issubmsg(f));
   if (SUBH_F(h, f)) return false;  /* Can't reset. */
   if (upb_msgdef_upcast(upb_handlers_msgdef(sub)) != upb_fielddef_subdef(f)) {
     return false;
@@ -2355,7 +2556,7 @@
 
 const upb_handlers *upb_handlers_getsubhandlers(const upb_handlers *h,
                                                 const upb_fielddef *f) {
-  assert(upb_fielddef_issubmsg(f));
+  UPB_ASSERT(upb_fielddef_issubmsg(f));
   return SUBH_F(h, f);
 }
 
@@ -2381,7 +2582,7 @@
     return false;
   }
   ok = upb_inttable_insertptr(&h->cleanup_, p, upb_value_fptr(func));
-  UPB_ASSERT_VAR(ok, ok);
+  UPB_ASSERT(ok);
   return true;
 }
 
@@ -2482,7 +2683,7 @@
     case UPB_TYPE_FLOAT: return UPB_HANDLER_FLOAT;
     case UPB_TYPE_DOUBLE: return UPB_HANDLER_DOUBLE;
     case UPB_TYPE_BOOL: return UPB_HANDLER_BOOL;
-    default: assert(false); return -1;  /* Invalid input. */
+    default: UPB_ASSERT(false); return -1;  /* Invalid input. */
   }
 }
 
@@ -2545,7 +2746,7 @@
       *s = f->selector_base;
       break;
   }
-  assert((size_t)*s < upb_fielddef_containingtype(f)->selector_count);
+  UPB_ASSERT((size_t)*s < upb_fielddef_containingtype(f)->selector_count);
   return true;
 }
 
@@ -2669,7 +2870,6 @@
 
 
 #include <setjmp.h>
-#include <stdlib.h>
 
 static void freeobj(upb_refcounted *o);
 
@@ -2745,8 +2945,31 @@
 /* UPB_DEBUG_REFS mode counts on being able to malloc() memory in some
  * code-paths that can normally never fail, like upb_refcounted_ref().  Since
  * we have no way to propagage out-of-memory errors back to the user, and since
- * these errors can only occur in UPB_DEBUG_REFS mode, we immediately fail. */
-#define CHECK_OOM(predicate) if (!(predicate)) { assert(predicate); exit(1); }
+ * these errors can only occur in UPB_DEBUG_REFS mode, we use an allocator that
+ * immediately aborts on failure (avoiding the global allocator, which might
+ * inject failures). */
+
+#include <stdlib.h>
+
+static void *upb_debugrefs_allocfunc(upb_alloc *alloc, void *ptr,
+                                     size_t oldsize, size_t size) {
+  UPB_UNUSED(alloc);
+  UPB_UNUSED(oldsize);
+  if (size == 0) {
+    free(ptr);
+    return NULL;
+  } else {
+    void *ret = realloc(ptr, size);
+
+    if (!ret) {
+      abort();
+    }
+
+    return ret;
+  }
+}
+
+upb_alloc upb_alloc_debugrefs = {&upb_debugrefs_allocfunc};
 
 typedef struct {
   int count;  /* How many refs there are (duplicates only allowed for ref2). */
@@ -2754,8 +2977,7 @@
 } trackedref;
 
 static trackedref *trackedref_new(bool is_ref2) {
-  trackedref *ret = malloc(sizeof(*ret));
-  CHECK_OOM(ret);
+  trackedref *ret = upb_malloc(&upb_alloc_debugrefs, sizeof(*ret));
   ret->count = 1;
   ret->is_ref2 = is_ref2;
   return ret;
@@ -2764,7 +2986,7 @@
 static void track(const upb_refcounted *r, const void *owner, bool ref2) {
   upb_value v;
 
-  assert(owner);
+  UPB_ASSERT(owner);
   if (owner == UPB_UNTRACKED_REF) return;
 
   upb_lock();
@@ -2775,20 +2997,20 @@
      * tracking behavior we get with regular refs.  Since ref2s only happen
      * inside upb, we'll accept this limitation until/unless there is a really
      * difficult upb-internal bug that can't be figured out without it. */
-    assert(ref2);
-    assert(ref->is_ref2);
+    UPB_ASSERT(ref2);
+    UPB_ASSERT(ref->is_ref2);
     ref->count++;
   } else {
     trackedref *ref = trackedref_new(ref2);
-    bool ok = upb_inttable_insertptr(r->refs, owner, upb_value_ptr(ref));
-    CHECK_OOM(ok);
+    upb_inttable_insertptr2(r->refs, owner, upb_value_ptr(ref),
+                            &upb_alloc_debugrefs);
     if (ref2) {
       /* We know this cast is safe when it is a ref2, because it's coming from
        * another refcounted object. */
       const upb_refcounted *from = owner;
-      assert(!upb_inttable_lookupptr(from->ref2s, r, NULL));
-      ok = upb_inttable_insertptr(from->ref2s, r, upb_value_ptr(NULL));
-      CHECK_OOM(ok);
+      UPB_ASSERT(!upb_inttable_lookupptr(from->ref2s, r, NULL));
+      upb_inttable_insertptr2(from->ref2s, r, upb_value_ptr(NULL),
+                              &upb_alloc_debugrefs);
     }
   }
   upb_unlock();
@@ -2799,15 +3021,15 @@
   bool found;
   trackedref *ref;
 
-  assert(owner);
+  UPB_ASSERT(owner);
   if (owner == UPB_UNTRACKED_REF) return;
 
   upb_lock();
   found = upb_inttable_lookupptr(r->refs, owner, &v);
   /* This assert will fail if an owner attempts to release a ref it didn't have. */
-  UPB_ASSERT_VAR(found, found);
+  UPB_ASSERT(found);
   ref = upb_value_getptr(v);
-  assert(ref->is_ref2 == ref2);
+  UPB_ASSERT(ref->is_ref2 == ref2);
   if (--ref->count == 0) {
     free(ref);
     upb_inttable_removeptr(r->refs, owner, NULL);
@@ -2816,7 +3038,7 @@
        * another refcounted object. */
       const upb_refcounted *from = owner;
       bool removed = upb_inttable_removeptr(from->ref2s, r, NULL);
-      assert(removed);
+      UPB_ASSERT(removed);
     }
   }
   upb_unlock();
@@ -2829,9 +3051,9 @@
 
   upb_lock();
   found = upb_inttable_lookupptr(r->refs, owner, &v);
-  UPB_ASSERT_VAR(found, found);
+  UPB_ASSERT(found);
   ref = upb_value_getptr(v);
-  assert(ref->is_ref2 == ref2);
+  UPB_ASSERT(ref->is_ref2 == ref2);
   upb_unlock();
 }
 
@@ -2846,19 +3068,17 @@
     upb_value v;
     upb_value count;
     trackedref *ref;
-    bool ok;
     bool found;
 
     upb_refcounted *to = (upb_refcounted*)upb_inttable_iter_key(&i);
 
     /* To get the count we need to look in the target's table. */
     found = upb_inttable_lookupptr(to->refs, owner, &v);
-    assert(found);
+    UPB_ASSERT(found);
     ref = upb_value_getptr(v);
     count = upb_value_int32(ref->count);
 
-    ok = upb_inttable_insertptr(tab, to, count);
-    CHECK_OOM(ok);
+    upb_inttable_insertptr2(tab, to, count, &upb_alloc_debugrefs);
   }
   upb_unlock();
 }
@@ -2876,62 +3096,50 @@
   bool removed;
   int32_t newcount;
 
-  assert(obj == s->obj);
-  assert(subobj);
+  UPB_ASSERT(obj == s->obj);
+  UPB_ASSERT(subobj);
   removed = upb_inttable_removeptr(ref2, subobj, &v);
   /* The following assertion will fail if the visit() function visits a subobj
    * that it did not have a ref2 on, or visits the same subobj too many times. */
-  assert(removed);
+  UPB_ASSERT(removed);
   newcount = upb_value_getint32(v) - 1;
   if (newcount > 0) {
-    upb_inttable_insert(ref2, (uintptr_t)subobj, upb_value_int32(newcount));
+    upb_inttable_insert2(ref2, (uintptr_t)subobj, upb_value_int32(newcount),
+                         &upb_alloc_debugrefs);
   }
 }
 
 static void visit(const upb_refcounted *r, upb_refcounted_visit *v,
                   void *closure) {
-  bool ok;
-
   /* In DEBUG_REFS mode we know what existing ref2 refs there are, so we know
    * exactly the set of nodes that visit() should visit.  So we verify visit()'s
    * correctness here. */
   check_state state;
   state.obj = r;
-  ok = upb_inttable_init(&state.ref2, UPB_CTYPE_INT32);
-  CHECK_OOM(ok);
+  upb_inttable_init2(&state.ref2, UPB_CTYPE_INT32, &upb_alloc_debugrefs);
   getref2s(r, &state.ref2);
 
   /* This should visit any children in the ref2 table. */
   if (r->vtbl->visit) r->vtbl->visit(r, visit_check, &state);
 
   /* This assertion will fail if the visit() function missed any children. */
-  assert(upb_inttable_count(&state.ref2) == 0);
-  upb_inttable_uninit(&state.ref2);
+  UPB_ASSERT(upb_inttable_count(&state.ref2) == 0);
+  upb_inttable_uninit2(&state.ref2, &upb_alloc_debugrefs);
   if (r->vtbl->visit) r->vtbl->visit(r, v, closure);
 }
 
-static bool trackinit(upb_refcounted *r) {
-  r->refs = malloc(sizeof(*r->refs));
-  r->ref2s = malloc(sizeof(*r->ref2s));
-  if (!r->refs || !r->ref2s) goto err1;
-
-  if (!upb_inttable_init(r->refs, UPB_CTYPE_PTR)) goto err1;
-  if (!upb_inttable_init(r->ref2s, UPB_CTYPE_PTR)) goto err2;
-  return true;
-
-err2:
-  upb_inttable_uninit(r->refs);
-err1:
-  free(r->refs);
-  free(r->ref2s);
-  return false;
+static void trackinit(upb_refcounted *r) {
+  r->refs = upb_malloc(&upb_alloc_debugrefs, sizeof(*r->refs));
+  r->ref2s = upb_malloc(&upb_alloc_debugrefs, sizeof(*r->ref2s));
+  upb_inttable_init2(r->refs, UPB_CTYPE_PTR, &upb_alloc_debugrefs);
+  upb_inttable_init2(r->ref2s, UPB_CTYPE_PTR, &upb_alloc_debugrefs);
 }
 
 static void trackfree(const upb_refcounted *r) {
-  upb_inttable_uninit(r->refs);
-  upb_inttable_uninit(r->ref2s);
-  free(r->refs);
-  free(r->ref2s);
+  upb_inttable_uninit2(r->refs, &upb_alloc_debugrefs);
+  upb_inttable_uninit2(r->ref2s, &upb_alloc_debugrefs);
+  upb_free(&upb_alloc_debugrefs, r->refs);
+  upb_free(&upb_alloc_debugrefs, r->ref2s);
 }
 
 #else
@@ -2954,9 +3162,8 @@
   UPB_UNUSED(ref2);
 }
 
-static bool trackinit(upb_refcounted *r) {
+static void trackinit(upb_refcounted *r) {
   UPB_UNUSED(r);
-  return true;
 }
 
 static void trackfree(const upb_refcounted *r) {
@@ -3023,7 +3230,7 @@
 static uint64_t getattr(const tarjan *t, const upb_refcounted *r) {
   upb_value v;
   bool found = upb_inttable_lookupptr(&t->objattr, r, &v);
-  UPB_ASSERT_VAR(found, found);
+  UPB_ASSERT(found);
   return upb_value_getuint64(v);
 }
 
@@ -3037,13 +3244,13 @@
 }
 
 static void set_gray(tarjan *t, const upb_refcounted *r) {
-  assert(color(t, r) == BLACK);
+  UPB_ASSERT(color(t, r) == BLACK);
   setattr(t, r, GRAY);
 }
 
 /* Pushes an obj onto the Tarjan stack and sets it to GREEN. */
 static void push(tarjan *t, const upb_refcounted *r) {
-  assert(color(t, r) == BLACK || color(t, r) == GRAY);
+  UPB_ASSERT(color(t, r) == BLACK || color(t, r) == GRAY);
   /* This defines the attr layout for the GREEN state.  "index" and "lowlink"
    * get 31 bits, which is plenty (limit of 2B objects frozen at a time). */
   setattr(t, r, GREEN | (t->index << 2) | (t->index << 33));
@@ -3058,7 +3265,7 @@
  * SCC group. */
 static upb_refcounted *pop(tarjan *t) {
   upb_refcounted *r = upb_value_getptr(upb_inttable_pop(&t->stack));
-  assert(color(t, r) == GREEN);
+  UPB_ASSERT(color(t, r) == GREEN);
   /* This defines the attr layout for nodes in the WHITE state.
    * Top of group stack is [group, NULL]; we point at group. */
   setattr(t, r, WHITE | (upb_inttable_count(&t->groups) - 2) << 8);
@@ -3066,19 +3273,19 @@
 }
 
 static void tarjan_newgroup(tarjan *t) {
-  uint32_t *group = malloc(sizeof(*group));
+  uint32_t *group = upb_gmalloc(sizeof(*group));
   if (!group) oom(t);
   /* Push group and empty group leader (we'll fill in leader later). */
   if (!upb_inttable_push(&t->groups, upb_value_ptr(group)) ||
       !upb_inttable_push(&t->groups, upb_value_ptr(NULL))) {
-    free(group);
+    upb_gfree(group);
     oom(t);
   }
   *group = 0;
 }
 
 static uint32_t idx(tarjan *t, const upb_refcounted *r) {
-  assert(color(t, r) == GREEN);
+  UPB_ASSERT(color(t, r) == GREEN);
   return (getattr(t, r) >> 2) & 0x7FFFFFFF;
 }
 
@@ -3091,7 +3298,7 @@
 }
 
 static void set_lowlink(tarjan *t, const upb_refcounted *r, uint32_t lowlink) {
-  assert(color(t, r) == GREEN);
+  UPB_ASSERT(color(t, r) == GREEN);
   setattr(t, r, ((uint64_t)lowlink << 33) | (getattr(t, r) & 0x1FFFFFFFF));
 }
 
@@ -3100,10 +3307,10 @@
   upb_value v;
   bool found;
 
-  assert(color(t, r) == WHITE);
+  UPB_ASSERT(color(t, r) == WHITE);
   groupnum = getattr(t, r) >> 8;
   found = upb_inttable_lookup(&t->groups, groupnum, &v);
-  UPB_ASSERT_VAR(found, found);
+  UPB_ASSERT(found);
   return upb_value_getptr(v);
 }
 
@@ -3114,10 +3321,10 @@
   upb_value v;
   bool found;
 
-  assert(color(t, r) == WHITE);
+  UPB_ASSERT(color(t, r) == WHITE);
   leader_slot = (getattr(t, r) >> 8) + 1;
   found = upb_inttable_lookup(&t->groups, leader_slot, &v);
-  UPB_ASSERT_VAR(found, found);
+  UPB_ASSERT(found);
   if (upb_value_getptr(v)) {
     return upb_value_getptr(v);
   } else {
@@ -3177,7 +3384,7 @@
 static void crossref(const upb_refcounted *r, const upb_refcounted *subobj,
                      void *_t) {
   tarjan *t = _t;
-  assert(color(t, r) > BLACK);
+  UPB_ASSERT(color(t, r) > BLACK);
   if (color(t, subobj) > BLACK && r->group != subobj->group) {
     /* Previously this ref was not reflected in subobj->group because they
      * were in the same group; now that they are split a ref must be taken. */
@@ -3245,13 +3452,13 @@
       upb_refcounted *move = obj->next;
       if (obj == move) {
         /* Removing the last object from a group. */
-        assert(*obj->group == obj->individual_count);
-        free(obj->group);
+        UPB_ASSERT(*obj->group == obj->individual_count);
+        upb_gfree(obj->group);
       } else {
         obj->next = move->next;
         /* This may decrease to zero; we'll collect GRAY objects (if any) that
          * remain in the group in the third pass. */
-        assert(*move->group >= move->individual_count);
+        UPB_ASSERT(*move->group >= move->individual_count);
         *move->group -= move->individual_count;
       }
 
@@ -3264,7 +3471,7 @@
         *move->group = move->individual_count;
       } else {
         /* Group already has at least one object in it. */
-        assert(leader->group == group(&t, move));
+        UPB_ASSERT(leader->group == group(&t, move));
         move->group = group(&t, move);
         move->next = leader->next;
         leader->next = move;
@@ -3302,7 +3509,7 @@
         /* We eagerly free() the group's count (since we can't easily determine
          * the group's remaining size it's the easiest way to ensure it gets
          * done). */
-        free(obj->group);
+        upb_gfree(obj->group);
 
         /* Visit to release ref2's (done in a separate pass since release_ref2
          * depends on o->group being unmodified so it can test merged()). */
@@ -3322,7 +3529,7 @@
   if (!ret) {
     upb_inttable_begin(&iter, &t.groups);
     for(; !upb_inttable_done(&iter); upb_inttable_next(&iter))
-      free(upb_value_getptr(upb_inttable_iter_value(&iter)));
+      upb_gfree(upb_value_getptr(upb_inttable_iter_value(&iter)));
   }
   upb_inttable_uninit(&t.groups);
 err3:
@@ -3346,7 +3553,7 @@
 
   if (merged(r, from)) return;
   *r->group += *from->group;
-  free(from->group);
+  upb_gfree(from->group);
   base = from;
 
   /* Set all refcount pointers in the "from" chain to the merged refcount.
@@ -3354,7 +3561,7 @@
    * TODO(haberman): this linear algorithm can result in an overall O(n^2) bound
    * if the user continuously extends a group by one object.  Prevent this by
    * using one of the techniques in this paper:
-   *     ftp://www.ncedc.org/outgoing/geomorph/dino/orals/p245-tarjan.pdf */
+   *     http://bioinfo.ict.ac.cn/~dbu/AlgorithmCourses/Lectures/Union-Find-Tarjan.pdf */
   do { from->group = r->group; } while ((from = from->next) != base);
 
   /* Merge the two circularly linked lists by swapping their next pointers. */
@@ -3371,7 +3578,7 @@
   UPB_UNUSED(closure);
   untrack(subobj, obj, true);
   if (!merged(obj, subobj)) {
-    assert(subobj->is_frozen);
+    UPB_ASSERT(subobj->is_frozen);
     unref(subobj);
   }
 }
@@ -3380,7 +3587,7 @@
   if (unrefgroup(r->group)) {
     const upb_refcounted *o;
 
-    free(r->group);
+    upb_gfree(r->group);
 
     /* In two passes, since release_ref2 needs a guarantee that any subobjs
      * are alive. */
@@ -3390,7 +3597,7 @@
     o = r;
     do {
       const upb_refcounted *next = o->next;
-      assert(o->is_frozen || o->individual_count == 0);
+      UPB_ASSERT(o->is_frozen || o->individual_count == 0);
       freeobj((upb_refcounted*)o);
       o = next;
     } while(o != r);
@@ -3414,9 +3621,9 @@
    * basically every program using upb. */
   const int x = 1;
 #ifdef UPB_BIG_ENDIAN
-  assert(*(char*)&x != 1);
+  UPB_ASSERT(*(char*)&x != 1);
 #else
-  assert(*(char*)&x == 1);
+  UPB_ASSERT(*(char*)&x == 1);
 #endif
 #endif
 
@@ -3424,13 +3631,10 @@
   r->vtbl = vtbl;
   r->individual_count = 0;
   r->is_frozen = false;
-  r->group = malloc(sizeof(*r->group));
+  r->group = upb_gmalloc(sizeof(*r->group));
   if (!r->group) return false;
   *r->group = 0;
-  if (!trackinit(r)) {
-    free(r->group);
-    return false;
-  }
+  trackinit(r);
   upb_refcounted_ref(r, owner);
   return true;
 }
@@ -3454,7 +3658,7 @@
 }
 
 void upb_refcounted_ref2(const upb_refcounted *r, upb_refcounted *from) {
-  assert(!from->is_frozen);  /* Non-const pointer implies this. */
+  UPB_ASSERT(!from->is_frozen);  /* Non-const pointer implies this. */
   track(r, from, true);
   if (r->is_frozen) {
     refgroup(r->group);
@@ -3464,18 +3668,18 @@
 }
 
 void upb_refcounted_unref2(const upb_refcounted *r, upb_refcounted *from) {
-  assert(!from->is_frozen);  /* Non-const pointer implies this. */
+  UPB_ASSERT(!from->is_frozen);  /* Non-const pointer implies this. */
   untrack(r, from, true);
   if (r->is_frozen) {
     unref(r);
   } else {
-    assert(merged(r, from));
+    UPB_ASSERT(merged(r, from));
   }
 }
 
 void upb_refcounted_donateref(
     const upb_refcounted *r, const void *from, const void *to) {
-  assert(from != to);
+  UPB_ASSERT(from != to);
   if (to != NULL)
     upb_refcounted_ref(r, to);
   if (from != NULL)
@@ -3489,15 +3693,16 @@
 bool upb_refcounted_freeze(upb_refcounted *const*roots, int n, upb_status *s,
                            int maxdepth) {
   int i;
+  bool ret;
   for (i = 0; i < n; i++) {
-    assert(!roots[i]->is_frozen);
+    UPB_ASSERT(!roots[i]->is_frozen);
   }
-  return freeze(roots, n, s, maxdepth);
+  ret = freeze(roots, n, s, maxdepth);
+  UPB_ASSERT(!s || ret == upb_ok(s));
+  return ret;
 }
 
 
-#include <stdlib.h>
-
 /* Fallback implementation if the shim is not specialized by the JIT. */
 #define SHIM_WRITER(type, ctype)                                              \
   bool upb_shim_set ## type (void *c, const void *hd, ctype val) {            \
@@ -3523,14 +3728,14 @@
   upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER;
   bool ok;
 
-  upb_shim_data *d = malloc(sizeof(*d));
+  upb_shim_data *d = upb_gmalloc(sizeof(*d));
   if (!d) return false;
   d->offset = offset;
   d->hasbit = hasbit;
 
   upb_handlerattr_sethandlerdata(&attr, d);
   upb_handlerattr_setalwaysok(&attr, true);
-  upb_handlers_addcleanup(h, d, free);
+  upb_handlers_addcleanup(h, d, upb_gfree);
 
 #define TYPE(u, l) \
   case UPB_TYPE_##u: \
@@ -3547,7 +3752,7 @@
     TYPE(DOUBLE, double);
     TYPE(FLOAT,  float);
     TYPE(BOOL,   bool);
-    default: assert(false); break;
+    default: UPB_ASSERT(false); break;
   }
 #undef TYPE
 
@@ -3581,7 +3786,6 @@
 }
 
 
-#include <stdlib.h>
 #include <string.h>
 
 static void upb_symtab_free(upb_refcounted *r) {
@@ -3593,13 +3797,17 @@
     upb_def_unref(def, s);
   }
   upb_strtable_uninit(&s->symtab);
-  free(s);
+  upb_gfree(s);
 }
 
-
 upb_symtab *upb_symtab_new(const void *owner) {
   static const struct upb_refcounted_vtbl vtbl = {NULL, &upb_symtab_free};
-  upb_symtab *s = malloc(sizeof(*s));
+
+  upb_symtab *s = upb_gmalloc(sizeof(*s));
+  if (!s) {
+    return NULL;
+  }
+
   upb_refcounted_init(upb_symtab_upcast_mutable(s), &vtbl, owner);
   upb_strtable_init(&s->symtab, UPB_CTYPE_PTR);
   return s;
@@ -3609,13 +3817,13 @@
   upb_refcounted *r;
   bool ok;
 
-  assert(!upb_symtab_isfrozen(s));
+  UPB_ASSERT(!upb_symtab_isfrozen(s));
   r = upb_symtab_upcast_mutable(s);
   /* The symtab does not take ref2's (see refcounted.h) on the defs, because
    * defs cannot refer back to the table and therefore cannot create cycles.  So
    * 0 will suffice for maxdepth here. */
   ok = upb_refcounted_freeze(&r, 1, NULL, 0);
-  UPB_ASSERT_VAR(ok, ok);
+  UPB_ASSERT(ok);
 }
 
 const upb_def *upb_symtab_lookup(const upb_symtab *s, const char *sym) {
@@ -3653,7 +3861,7 @@
     /* Remove components from base until we find an entry or run out.
      * TODO: This branch is totally broken, but currently not used. */
     (void)base;
-    assert(false);
+    UPB_ASSERT(false);
     return NULL;
   }
 }
@@ -3715,7 +3923,7 @@
     upb_value v;
     const upb_msgdef *m;
 
-    assert(upb_def_isfrozen(def));
+    UPB_ASSERT(upb_def_isfrozen(def));
     if (def->type == UPB_DEF_FIELD) continue;
     if (upb_strtable_lookup(addtab, upb_def_fullname(def), &v)) {
       need_dup = true;
@@ -3773,15 +3981,24 @@
 
 /* TODO(haberman): we need a lot more testing of error conditions.
  * The came_from_user stuff in particular is not tested. */
-bool upb_symtab_add(upb_symtab *s, upb_def *const*defs, int n, void *ref_donor,
-                    upb_status *status) {
-  int i;
+static bool symtab_add(upb_symtab *s, upb_def *const*defs, size_t n,
+                       void *ref_donor, upb_refcounted *freeze_also,
+                       upb_status *status) {
+  size_t i;
+  size_t add_n;
+  size_t freeze_n;
   upb_strtable_iter iter;
+  upb_refcounted **add_objs = NULL;
   upb_def **add_defs = NULL;
+  size_t add_objs_size;
   upb_strtable addtab;
   upb_inttable seen;
 
-  assert(!upb_symtab_isfrozen(s));
+  if (n == 0 && !freeze_also) {
+    return true;
+  }
+
+  UPB_ASSERT(!upb_symtab_isfrozen(s));
   if (!upb_strtable_init(&addtab, UPB_CTYPE_PTR)) {
     upb_status_seterrmsg(status, "out of memory");
     return false;
@@ -3797,7 +4014,7 @@
       upb_status_seterrmsg(status, "added defs must be mutable");
       goto err;
     }
-    assert(!upb_def_isfrozen(def));
+    UPB_ASSERT(!upb_def_isfrozen(def));
     fullname = upb_def_fullname(def);
     if (!fullname) {
       upb_status_seterrmsg(
@@ -3841,7 +4058,7 @@
     if (!f) continue;
     msgname = upb_fielddef_containingtypename(f);
     /* We validated this earlier in this function. */
-    assert(msgname);
+    UPB_ASSERT(msgname);
 
     /* If the extendee name is absolutely qualified, move past the initial ".".
      * TODO(haberman): it is not obvious what it would mean if this was not
@@ -3922,15 +4139,38 @@
     }
   }
 
-  /* We need an array of the defs in addtab, for passing to upb_def_freeze. */
-  add_defs = malloc(sizeof(void*) * upb_strtable_count(&addtab));
-  if (add_defs == NULL) goto oom_err;
-  upb_strtable_begin(&iter, &addtab);
-  for (n = 0; !upb_strtable_done(&iter); upb_strtable_next(&iter)) {
-    add_defs[n++] = upb_value_getptr(upb_strtable_iter_value(&iter));
+  /* We need an array of the defs in addtab, for passing to
+   * upb_refcounted_freeze(). */
+  add_objs_size = upb_strtable_count(&addtab);
+  if (freeze_also) {
+    add_objs_size++;
   }
 
-  if (!upb_def_freeze(add_defs, n, status)) goto err;
+  add_defs = upb_gmalloc(sizeof(void*) * add_objs_size);
+  if (add_defs == NULL) goto oom_err;
+  upb_strtable_begin(&iter, &addtab);
+  for (add_n = 0; !upb_strtable_done(&iter); upb_strtable_next(&iter)) {
+    add_defs[add_n++] = upb_value_getptr(upb_strtable_iter_value(&iter));
+  }
+
+  /* Validate defs. */
+  if (!_upb_def_validate(add_defs, add_n, status)) {
+    goto err;
+  }
+
+  /* Cheat a little and give the array a new type.
+   * This is probably undefined behavior, but this code will be deleted soon. */
+  add_objs = (upb_refcounted**)add_defs;
+
+  freeze_n = add_n;
+  if (freeze_also) {
+    add_objs[freeze_n++] = freeze_also;
+  }
+
+  if (!upb_refcounted_freeze(add_objs, freeze_n, status,
+                             UPB_MAX_MESSAGE_DEPTH * 2)) {
+    goto err;
+  }
 
   /* This must be delayed until all errors have been detected, since error
    * recovery code uses this table to cleanup defs. */
@@ -3938,8 +4178,8 @@
 
   /* TODO(haberman) we don't properly handle errors after this point (like
    * OOM in upb_strtable_insert() below). */
-  for (i = 0; i < n; i++) {
-    upb_def *def = add_defs[i];
+  for (i = 0; i < add_n; i++) {
+    upb_def *def = (upb_def*)add_objs[i];
     const char *name = upb_def_fullname(def);
     upb_value v;
     bool success;
@@ -3949,9 +4189,9 @@
       upb_def_unref(def, s);
     }
     success = upb_strtable_insert(&s->symtab, name, upb_value_ptr(def));
-    UPB_ASSERT_VAR(success, success == true);
+    UPB_ASSERT(success == true);
   }
-  free(add_defs);
+  upb_gfree(add_defs);
   return true;
 
 oom_err:
@@ -3972,11 +4212,40 @@
     }
   }
   upb_strtable_uninit(&addtab);
-  free(add_defs);
-  assert(!upb_ok(status));
+  upb_gfree(add_defs);
+  UPB_ASSERT(!upb_ok(status));
   return false;
 }
 
+bool upb_symtab_add(upb_symtab *s, upb_def *const*defs, size_t n,
+                    void *ref_donor, upb_status *status) {
+  return symtab_add(s, defs, n, ref_donor, NULL, status);
+}
+
+bool upb_symtab_addfile(upb_symtab *s, upb_filedef *file, upb_status *status) {
+  size_t n;
+  size_t i;
+  upb_def **defs;
+  bool ret;
+
+  n = upb_filedef_defcount(file);
+  defs = upb_gmalloc(sizeof(*defs) * n);
+
+  if (defs == NULL) {
+    upb_status_seterrmsg(status, "Out of memory");
+    return false;
+  }
+
+  for (i = 0; i < n; i++) {
+    defs[i] = upb_filedef_mutabledef(file, i);
+  }
+
+  ret = symtab_add(s, defs, n, NULL, upb_filedef_upcast_mutable(file), status);
+
+  upb_gfree(defs);
+  return ret;
+}
+
 /* Iteration. */
 
 static void advance_to_matching(upb_symtab_iter *iter) {
@@ -4015,7 +4284,6 @@
 */
 
 
-#include <stdlib.h>
 #include <string.h>
 
 #define UPB_MAXARRSIZE 16  /* 64k. */
@@ -4024,6 +4292,12 @@
 #define ARRAY_SIZE(x) \
     ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))
 
+static void upb_check_alloc(upb_table *t, upb_alloc *a) {
+  UPB_UNUSED(t);
+  UPB_UNUSED(a);
+  UPB_ASSERT_DEBUGVAR(t->alloc == a);
+}
+
 static const double MAX_LOAD = 0.85;
 
 /* The minimum utilization of the array part of a mixed hash/array table.  This
@@ -4041,11 +4315,11 @@
   return UPB_MIN(UPB_MAXARRSIZE, ret);
 }
 
-char *upb_strdup(const char *s) {
-  return upb_strdup2(s, strlen(s));
+char *upb_strdup(const char *s, upb_alloc *a) {
+  return upb_strdup2(s, strlen(s), a);
 }
 
-char *upb_strdup2(const char *s, size_t len) {
+char *upb_strdup2(const char *s, size_t len, upb_alloc *a) {
   size_t n;
   char *p;
 
@@ -4054,7 +4328,7 @@
   /* Always null-terminate, even if binary data; but don't rely on the input to
    * have a null-terminating byte since it may be a raw binary buffer. */
   n = len + 1;
-  p = malloc(n);
+  p = upb_malloc(a, n);
   if (p) {
     memcpy(p, s, len);
     p[len] = 0;
@@ -4095,19 +4369,27 @@
 }
 
 static bool isfull(upb_table *t) {
-  return (double)(t->count + 1) / upb_table_size(t) > MAX_LOAD;
+  if (upb_table_size(t) == 0) {
+    return true;
+  } else {
+    return ((double)(t->count + 1) / upb_table_size(t)) > MAX_LOAD;
+  }
 }
 
-static bool init(upb_table *t, upb_ctype_t ctype, uint8_t size_lg2) {
+static bool init(upb_table *t, upb_ctype_t ctype, uint8_t size_lg2,
+                 upb_alloc *a) {
   size_t bytes;
 
   t->count = 0;
   t->ctype = ctype;
   t->size_lg2 = size_lg2;
   t->mask = upb_table_size(t) ? upb_table_size(t) - 1 : 0;
+#ifndef NDEBUG
+  t->alloc = a;
+#endif
   bytes = upb_table_size(t) * sizeof(upb_tabent);
   if (bytes > 0) {
-    t->entries = malloc(bytes);
+    t->entries = upb_malloc(a, bytes);
     if (!t->entries) return false;
     memset(mutable_entries(t), 0, bytes);
   } else {
@@ -4116,11 +4398,14 @@
   return true;
 }
 
-static void uninit(upb_table *t) { free(mutable_entries(t)); }
+static void uninit(upb_table *t, upb_alloc *a) {
+  upb_check_alloc(t, a);
+  upb_free(a, mutable_entries(t));
+}
 
 static upb_tabent *emptyent(upb_table *t) {
   upb_tabent *e = mutable_entries(t) + upb_table_size(t);
-  while (1) { if (upb_tabent_isempty(--e)) return e; assert(e > t->entries); }
+  while (1) { if (upb_tabent_isempty(--e)) return e; UPB_ASSERT(e > t->entries); }
 }
 
 static upb_tabent *getentry_mutable(upb_table *t, uint32_t hash) {
@@ -4165,10 +4450,8 @@
   upb_tabent *mainpos_e;
   upb_tabent *our_e;
 
-  UPB_UNUSED(eql);
-  UPB_UNUSED(key);
-  assert(findentry(t, key, hash, eql) == NULL);
-  assert(val.ctype == t->ctype);
+  UPB_ASSERT(findentry(t, key, hash, eql) == NULL);
+  UPB_ASSERT_DEBUGVAR(val.ctype == t->ctype);
 
   t->count++;
   mainpos_e = getentry_mutable(t, hash);
@@ -4195,7 +4478,7 @@
       *new_e = *mainpos_e;  /* copies next. */
       while (chain->next != mainpos_e) {
         chain = (upb_tabent*)chain->next;
-        assert(chain);
+        UPB_ASSERT(chain);
       }
       chain->next = new_e;
       our_e = mainpos_e;
@@ -4204,7 +4487,7 @@
   }
   our_e->key = tabkey;
   our_e->val.val = val.val;
-  assert(findentry(t, key, hash, eql) == our_e);
+  UPB_ASSERT(findentry(t, key, hash, eql) == our_e);
 }
 
 static bool rm(upb_table *t, lookupkey_t key, upb_value *val,
@@ -4214,38 +4497,33 @@
   if (eql(chain->key, key)) {
     /* Element to remove is at the head of its chain. */
     t->count--;
-    if (val) {
-      _upb_value_setval(val, chain->val.val, t->ctype);
-    }
+    if (val) _upb_value_setval(val, chain->val.val, t->ctype);
+    if (removed) *removed = chain->key;
     if (chain->next) {
       upb_tabent *move = (upb_tabent*)chain->next;
       *chain = *move;
-      if (removed) *removed = move->key;
       move->key = 0;  /* Make the slot empty. */
     } else {
-      if (removed) *removed = chain->key;
       chain->key = 0;  /* Make the slot empty. */
     }
     return true;
   } else {
     /* Element to remove is either in a non-head position or not in the
      * table. */
-    while (chain->next && !eql(chain->next->key, key))
+    while (chain->next && !eql(chain->next->key, key)) {
       chain = (upb_tabent*)chain->next;
+    }
     if (chain->next) {
       /* Found element to remove. */
-      upb_tabent *rm;
-
-      if (val) {
-        _upb_value_setval(val, chain->next->val.val, t->ctype);
-      }
-      rm = (upb_tabent*)chain->next;
-      if (removed) *removed = rm->key;
-      rm->key = 0;
-      chain->next = rm->next;
+      upb_tabent *rm = (upb_tabent*)chain->next;
       t->count--;
+      if (val) _upb_value_setval(val, chain->next->val.val, t->ctype);
+      if (removed) *removed = rm->key;
+      rm->key = 0;  /* Make the slot empty. */
+      chain->next = rm->next;
       return true;
     } else {
+      /* Element to remove is not in the table. */
       return false;
     }
   }
@@ -4269,8 +4547,8 @@
 
 /* A simple "subclass" of upb_table that only adds a hash function for strings. */
 
-static upb_tabkey strcopy(lookupkey_t k2) {
-  char *str = malloc(k2.str.len + sizeof(uint32_t) + 1);
+static upb_tabkey strcopy(lookupkey_t k2, upb_alloc *a) {
+  char *str = upb_malloc(a, k2.str.len + sizeof(uint32_t) + 1);
   if (str == NULL) return 0;
   memcpy(str, &k2.str.len, sizeof(uint32_t));
   memcpy(str + sizeof(uint32_t), k2.str.str, k2.str.len + 1);
@@ -4289,51 +4567,56 @@
   return len == k2.str.len && memcmp(str, k2.str.str, len) == 0;
 }
 
-bool upb_strtable_init(upb_strtable *t, upb_ctype_t ctype) {
-  return init(&t->t, ctype, 2);
+bool upb_strtable_init2(upb_strtable *t, upb_ctype_t ctype, upb_alloc *a) {
+  return init(&t->t, ctype, 2, a);
 }
 
-void upb_strtable_uninit(upb_strtable *t) {
+void upb_strtable_uninit2(upb_strtable *t, upb_alloc *a) {
   size_t i;
   for (i = 0; i < upb_table_size(&t->t); i++)
-    free((void*)t->t.entries[i].key);
-  uninit(&t->t);
+    upb_free(a, (void*)t->t.entries[i].key);
+  uninit(&t->t, a);
 }
 
-bool upb_strtable_resize(upb_strtable *t, size_t size_lg2) {
+bool upb_strtable_resize(upb_strtable *t, size_t size_lg2, upb_alloc *a) {
   upb_strtable new_table;
   upb_strtable_iter i;
 
-  if (!init(&new_table.t, t->t.ctype, size_lg2))
+  upb_check_alloc(&t->t, a);
+
+  if (!init(&new_table.t, t->t.ctype, size_lg2, a))
     return false;
   upb_strtable_begin(&i, t);
   for ( ; !upb_strtable_done(&i); upb_strtable_next(&i)) {
-    upb_strtable_insert2(
+    upb_strtable_insert3(
         &new_table,
         upb_strtable_iter_key(&i),
         upb_strtable_iter_keylength(&i),
-        upb_strtable_iter_value(&i));
+        upb_strtable_iter_value(&i),
+        a);
   }
-  upb_strtable_uninit(t);
+  upb_strtable_uninit2(t, a);
   *t = new_table;
   return true;
 }
 
-bool upb_strtable_insert2(upb_strtable *t, const char *k, size_t len,
-                          upb_value v) {
+bool upb_strtable_insert3(upb_strtable *t, const char *k, size_t len,
+                          upb_value v, upb_alloc *a) {
   lookupkey_t key;
   upb_tabkey tabkey;
   uint32_t hash;
 
+  upb_check_alloc(&t->t, a);
+
   if (isfull(&t->t)) {
     /* Need to resize.  New table of double the size, add old elements to it. */
-    if (!upb_strtable_resize(t, t->t.size_lg2 + 1)) {
+    if (!upb_strtable_resize(t, t->t.size_lg2 + 1, a)) {
       return false;
     }
   }
 
   key = strkey2(k, len);
-  tabkey = strcopy(key);
+  tabkey = strcopy(key, a);
   if (tabkey == 0) return false;
 
   hash = MurmurHash2(key.str.str, key.str.len, 0);
@@ -4347,12 +4630,12 @@
   return lookup(&t->t, strkey2(key, len), v, hash, &streql);
 }
 
-bool upb_strtable_remove2(upb_strtable *t, const char *key, size_t len,
-                         upb_value *val) {
-  uint32_t hash = MurmurHash2(key, strlen(key), 0);
+bool upb_strtable_remove3(upb_strtable *t, const char *key, size_t len,
+                         upb_value *val, upb_alloc *alloc) {
+  uint32_t hash = MurmurHash2(key, len, 0);
   upb_tabkey tabkey;
   if (rm(&t->t, strkey2(key, len), val, &tabkey, hash, &streql)) {
-    free((void*)tabkey);
+    upb_free(alloc, (void*)tabkey);
     return true;
   } else {
     return false;
@@ -4379,20 +4662,20 @@
          upb_tabent_isempty(str_tabent(i));
 }
 
-const char *upb_strtable_iter_key(upb_strtable_iter *i) {
-  assert(!upb_strtable_done(i));
+const char *upb_strtable_iter_key(const upb_strtable_iter *i) {
+  UPB_ASSERT(!upb_strtable_done(i));
   return upb_tabstr(str_tabent(i)->key, NULL);
 }
 
-size_t upb_strtable_iter_keylength(upb_strtable_iter *i) {
+size_t upb_strtable_iter_keylength(const upb_strtable_iter *i) {
   uint32_t len;
-  assert(!upb_strtable_done(i));
+  UPB_ASSERT(!upb_strtable_done(i));
   upb_tabstr(str_tabent(i)->key, &len);
   return len;
 }
 
 upb_value upb_strtable_iter_value(const upb_strtable_iter *i) {
-  assert(!upb_strtable_done(i));
+  UPB_ASSERT(!upb_strtable_done(i));
   return _upb_value_val(str_tabent(i)->val.val, i->t->t.ctype);
 }
 
@@ -4451,26 +4734,26 @@
     upb_inttable_iter i;
     upb_inttable_begin(&i, t);
     for(; !upb_inttable_done(&i); upb_inttable_next(&i), count++) {
-      assert(upb_inttable_lookup(t, upb_inttable_iter_key(&i), NULL));
+      UPB_ASSERT(upb_inttable_lookup(t, upb_inttable_iter_key(&i), NULL));
     }
-    assert(count == upb_inttable_count(t));
+    UPB_ASSERT(count == upb_inttable_count(t));
   }
 #endif
 }
 
 bool upb_inttable_sizedinit(upb_inttable *t, upb_ctype_t ctype,
-                            size_t asize, int hsize_lg2) {
+                            size_t asize, int hsize_lg2, upb_alloc *a) {
   size_t array_bytes;
 
-  if (!init(&t->t, ctype, hsize_lg2)) return false;
+  if (!init(&t->t, ctype, hsize_lg2, a)) return false;
   /* Always make the array part at least 1 long, so that we know key 0
    * won't be in the hash part, which simplifies things. */
   t->array_size = UPB_MAX(1, asize);
   t->array_count = 0;
   array_bytes = t->array_size * sizeof(upb_value);
-  t->array = malloc(array_bytes);
+  t->array = upb_malloc(a, array_bytes);
   if (!t->array) {
-    uninit(&t->t);
+    uninit(&t->t, a);
     return false;
   }
   memset(mutable_array(t), 0xff, array_bytes);
@@ -4478,25 +4761,25 @@
   return true;
 }
 
-bool upb_inttable_init(upb_inttable *t, upb_ctype_t ctype) {
-  return upb_inttable_sizedinit(t, ctype, 0, 4);
+bool upb_inttable_init2(upb_inttable *t, upb_ctype_t ctype, upb_alloc *a) {
+  return upb_inttable_sizedinit(t, ctype, 0, 4, a);
 }
 
-void upb_inttable_uninit(upb_inttable *t) {
-  uninit(&t->t);
-  free(mutable_array(t));
+void upb_inttable_uninit2(upb_inttable *t, upb_alloc *a) {
+  uninit(&t->t, a);
+  upb_free(a, mutable_array(t));
 }
 
-bool upb_inttable_insert(upb_inttable *t, uintptr_t key, upb_value val) {
-  /* XXX: Table can't store value (uint64_t)-1.  Need to somehow statically
-   * guarantee that this is not necessary, or fix the limitation. */
+bool upb_inttable_insert2(upb_inttable *t, uintptr_t key, upb_value val,
+                          upb_alloc *a) {
   upb_tabval tabval;
   tabval.val = val.val;
-  UPB_UNUSED(tabval);
-  assert(upb_arrhas(tabval));
+  UPB_ASSERT(upb_arrhas(tabval));  /* This will reject (uint64_t)-1.  Fix this. */
+
+  upb_check_alloc(&t->t, a);
 
   if (key < t->array_size) {
-    assert(!upb_arrhas(t->array[key]));
+    UPB_ASSERT(!upb_arrhas(t->array[key]));
     t->array_count++;
     mutable_array(t)[key].val = val.val;
   } else {
@@ -4504,8 +4787,11 @@
       /* Need to resize the hash part, but we re-use the array part. */
       size_t i;
       upb_table new_table;
-      if (!init(&new_table, t->t.ctype, t->t.size_lg2 + 1))
+
+      if (!init(&new_table, t->t.ctype, t->t.size_lg2 + 1, a)) {
         return false;
+      }
+
       for (i = begin(&t->t); i < upb_table_size(&t->t); i = next(&t->t, i)) {
         const upb_tabent *e = &t->t.entries[i];
         uint32_t hash;
@@ -4516,9 +4802,9 @@
         insert(&new_table, intkey(e->key), e->key, v, hash, &inthash, &inteql);
       }
 
-      assert(t->t.count == new_table.count);
+      UPB_ASSERT(t->t.count == new_table.count);
 
-      uninit(&t->t);
+      uninit(&t->t, a);
       t->t = new_table;
     }
     insert(&t->t, intkey(key), key, val, upb_inthash(key), &inthash, &inteql);
@@ -4556,27 +4842,28 @@
       success = false;
     }
   } else {
-    upb_tabkey removed;
-    uint32_t hash = upb_inthash(key);
-    success = rm(&t->t, intkey(key), val, &removed, hash, &inteql);
+    success = rm(&t->t, intkey(key), val, NULL, upb_inthash(key), &inteql);
   }
   check(t);
   return success;
 }
 
-bool upb_inttable_push(upb_inttable *t, upb_value val) {
-  return upb_inttable_insert(t, upb_inttable_count(t), val);
+bool upb_inttable_push2(upb_inttable *t, upb_value val, upb_alloc *a) {
+  upb_check_alloc(&t->t, a);
+  return upb_inttable_insert2(t, upb_inttable_count(t), val, a);
 }
 
 upb_value upb_inttable_pop(upb_inttable *t) {
   upb_value val;
   bool ok = upb_inttable_remove(t, upb_inttable_count(t) - 1, &val);
-  UPB_ASSERT_VAR(ok, ok);
+  UPB_ASSERT(ok);
   return val;
 }
 
-bool upb_inttable_insertptr(upb_inttable *t, const void *key, upb_value val) {
-  return upb_inttable_insert(t, (uintptr_t)key, val);
+bool upb_inttable_insertptr2(upb_inttable *t, const void *key, upb_value val,
+                             upb_alloc *a) {
+  upb_check_alloc(&t->t, a);
+  return upb_inttable_insert2(t, (uintptr_t)key, val, a);
 }
 
 bool upb_inttable_lookupptr(const upb_inttable *t, const void *key,
@@ -4588,77 +4875,74 @@
   return upb_inttable_remove(t, (uintptr_t)key, val);
 }
 
-void upb_inttable_compact(upb_inttable *t) {
-  /* Create a power-of-two histogram of the table keys. */
-  int counts[UPB_MAXARRSIZE + 1] = {0};
-  uintptr_t max_key = 0;
+void upb_inttable_compact2(upb_inttable *t, upb_alloc *a) {
+  /* A power-of-two histogram of the table keys. */
+  size_t counts[UPB_MAXARRSIZE + 1] = {0};
+
+  /* The max key in each bucket. */
+  uintptr_t max[UPB_MAXARRSIZE + 1] = {0};
+
   upb_inttable_iter i;
-  size_t arr_size;
-  int arr_count;
+  size_t arr_count;
+  int size_lg2;
   upb_inttable new_t;
 
+  upb_check_alloc(&t->t, a);
+
   upb_inttable_begin(&i, t);
   for (; !upb_inttable_done(&i); upb_inttable_next(&i)) {
     uintptr_t key = upb_inttable_iter_key(&i);
-    if (key > max_key) {
-      max_key = key;
-    }
-    counts[log2ceil(key)]++;
+    int bucket = log2ceil(key);
+    max[bucket] = UPB_MAX(max[bucket], key);
+    counts[bucket]++;
   }
 
-  arr_size = 1;
+  /* Find the largest power of two that satisfies the MIN_DENSITY
+   * definition (while actually having some keys). */
   arr_count = upb_inttable_count(t);
 
-  if (upb_inttable_count(t) >= max_key * MIN_DENSITY) {
-    /* We can put 100% of the entries in the array part. */
-    arr_size = max_key + 1;
-  } else {
-    /* Find the largest power of two that satisfies the MIN_DENSITY
-     * definition. */
-    int size_lg2;
-    for (size_lg2 = ARRAY_SIZE(counts) - 1; size_lg2 > 1; size_lg2--) {
-      arr_size = 1 << size_lg2;
-      arr_count -= counts[size_lg2];
-      if (arr_count >= arr_size * MIN_DENSITY) {
-        break;
-      }
+  for (size_lg2 = ARRAY_SIZE(counts) - 1; size_lg2 > 0; size_lg2--) {
+    if (counts[size_lg2] == 0) {
+      /* We can halve again without losing any entries. */
+      continue;
+    } else if (arr_count >= (1 << size_lg2) * MIN_DENSITY) {
+      break;
     }
+
+    arr_count -= counts[size_lg2];
   }
 
-  /* Array part must always be at least 1 entry large to catch lookups of key
-   * 0.  Key 0 must always be in the array part because "0" in the hash part
-   * denotes an empty entry. */
-  arr_size = UPB_MAX(arr_size, 1);
+  UPB_ASSERT(arr_count <= upb_inttable_count(t));
 
   {
     /* Insert all elements into new, perfectly-sized table. */
-    int hash_count = upb_inttable_count(t) - arr_count;
-    int hash_size = hash_count ? (hash_count / MAX_LOAD) + 1 : 0;
-    int hashsize_lg2 = log2ceil(hash_size);
+    size_t arr_size = max[size_lg2] + 1;  /* +1 so arr[max] will fit. */
+    size_t hash_count = upb_inttable_count(t) - arr_count;
+    size_t hash_size = hash_count ? (hash_count / MAX_LOAD) + 1 : 0;
+    size_t hashsize_lg2 = log2ceil(hash_size);
 
-    assert(hash_count >= 0);
-    upb_inttable_sizedinit(&new_t, t->t.ctype, arr_size, hashsize_lg2);
+    upb_inttable_sizedinit(&new_t, t->t.ctype, arr_size, hashsize_lg2, a);
     upb_inttable_begin(&i, t);
     for (; !upb_inttable_done(&i); upb_inttable_next(&i)) {
       uintptr_t k = upb_inttable_iter_key(&i);
-      upb_inttable_insert(&new_t, k, upb_inttable_iter_value(&i));
+      upb_inttable_insert2(&new_t, k, upb_inttable_iter_value(&i), a);
     }
-    assert(new_t.array_size == arr_size);
-    assert(new_t.t.size_lg2 == hashsize_lg2);
+    UPB_ASSERT(new_t.array_size == arr_size);
+    UPB_ASSERT(new_t.t.size_lg2 == hashsize_lg2);
   }
-  upb_inttable_uninit(t);
+  upb_inttable_uninit2(t, a);
   *t = new_t;
 }
 
 /* Iteration. */
 
 static const upb_tabent *int_tabent(const upb_inttable_iter *i) {
-  assert(!i->array_part);
+  UPB_ASSERT(!i->array_part);
   return &i->t->t.entries[i->index];
 }
 
 static upb_tabval int_arrent(const upb_inttable_iter *i) {
-  assert(i->array_part);
+  UPB_ASSERT(i->array_part);
   return i->t->array[i->index];
 }
 
@@ -4695,12 +4979,12 @@
 }
 
 uintptr_t upb_inttable_iter_key(const upb_inttable_iter *i) {
-  assert(!upb_inttable_done(i));
+  UPB_ASSERT(!upb_inttable_done(i));
   return i->array_part ? i->index : int_tabent(i)->key;
 }
 
 upb_value upb_inttable_iter_value(const upb_inttable_iter *i) {
-  assert(!upb_inttable_done(i));
+  UPB_ASSERT(!upb_inttable_done(i));
   return _upb_value_val(
       i->array_part ? i->t->array[i->index].val : int_tabent(i)->val.val,
       i->t->t.ctype);
@@ -4918,10 +5202,23 @@
 static void nullz(upb_status *status) {
   const char *ellipsis = "...";
   size_t len = strlen(ellipsis);
-  assert(sizeof(status->msg) > len);
+  UPB_ASSERT(sizeof(status->msg) > len);
   memcpy(status->msg + sizeof(status->msg) - len, ellipsis, len);
 }
 
+
+/* upb_upberr *****************************************************************/
+
+upb_errorspace upb_upberr = {"upb error"};
+
+void upb_upberr_setoom(upb_status *status) {
+  status->error_space_ = &upb_upberr;
+  upb_status_seterrmsg(status, "Out of memory");
+}
+
+
+/* upb_status *****************************************************************/
+
 void upb_status_clear(upb_status *status) {
   if (!status) return;
   status->ok_ = true;
@@ -4960,311 +5257,604 @@
   nullz(status);
 }
 
-void upb_status_seterrcode(upb_status *status, upb_errorspace *space,
-                           int code) {
-  if (!status) return;
-  status->ok_ = false;
-  status->error_space_ = space;
-  status->code_ = code;
-  space->set_message(status, code);
-}
-
 void upb_status_copy(upb_status *to, const upb_status *from) {
   if (!to) return;
   *to = *from;
 }
-/* This file was generated by upbc (the upb compiler).
+
+
+/* upb_alloc ******************************************************************/
+
+static void *upb_global_allocfunc(upb_alloc *alloc, void *ptr, size_t oldsize,
+                                  size_t size) {
+  UPB_UNUSED(alloc);
+  UPB_UNUSED(oldsize);
+  if (size == 0) {
+    free(ptr);
+    return NULL;
+  } else {
+    return realloc(ptr, size);
+  }
+}
+
+upb_alloc upb_alloc_global = {&upb_global_allocfunc};
+
+
+/* upb_arena ******************************************************************/
+
+/* Be conservative and choose 16 in case anyone is using SSE. */
+static const size_t maxalign = 16;
+
+static size_t align_up(size_t size) {
+  return ((size + maxalign - 1) / maxalign) * maxalign;
+}
+
+typedef struct mem_block {
+  struct mem_block *next;
+  size_t size;
+  size_t used;
+  bool owned;
+  /* Data follows. */
+} mem_block;
+
+typedef struct cleanup_ent {
+  struct cleanup_ent *next;
+  upb_cleanup_func *cleanup;
+  void *ud;
+} cleanup_ent;
+
+static void upb_arena_addblock(upb_arena *a, void *ptr, size_t size,
+                               bool owned) {
+  mem_block *block = ptr;
+
+  block->next = a->block_head;
+  block->size = size;
+  block->used = align_up(sizeof(mem_block));
+  block->owned = owned;
+
+  a->block_head = block;
+
+  /* TODO(haberman): ASAN poison. */
+}
+
+
+static mem_block *upb_arena_allocblock(upb_arena *a, size_t size) {
+  size_t block_size = UPB_MAX(size, a->next_block_size) + sizeof(mem_block);
+  mem_block *block = upb_malloc(a->block_alloc, block_size);
+
+  if (!block) {
+    return NULL;
+  }
+
+  upb_arena_addblock(a, block, block_size, true);
+  a->next_block_size = UPB_MIN(block_size * 2, a->max_block_size);
+
+  return block;
+}
+
+static void *upb_arena_doalloc(upb_alloc *alloc, void *ptr, size_t oldsize,
+                               size_t size) {
+  upb_arena *a = (upb_arena*)alloc;  /* upb_alloc is initial member. */
+  mem_block *block = a->block_head;
+  void *ret;
+
+  if (size == 0) {
+    return NULL;  /* We are an arena, don't need individual frees. */
+  }
+
+  size = align_up(size);
+
+  /* TODO(haberman): special-case if this is a realloc of the last alloc? */
+
+  if (!block || block->size - block->used < size) {
+    /* Slow path: have to allocate a new block. */
+    block = upb_arena_allocblock(a, size);
+
+    if (!block) {
+      return NULL;  /* Out of memory. */
+    }
+  }
+
+  ret = (char*)block + block->used;
+  block->used += size;
+
+  if (oldsize > 0) {
+    memcpy(ret, ptr, oldsize);  /* Preserve existing data. */
+  }
+
+  /* TODO(haberman): ASAN unpoison. */
+
+  a->bytes_allocated += size;
+  return ret;
+}
+
+/* Public Arena API ***********************************************************/
+
+void upb_arena_init(upb_arena *a) {
+  a->alloc.func = &upb_arena_doalloc;
+  a->block_alloc = &upb_alloc_global;
+  a->bytes_allocated = 0;
+  a->next_block_size = 256;
+  a->max_block_size = 16384;
+  a->cleanup_head = NULL;
+  a->block_head = NULL;
+}
+
+void upb_arena_init2(upb_arena *a, void *mem, size_t size, upb_alloc *alloc) {
+  upb_arena_init(a);
+
+  if (size > sizeof(mem_block)) {
+    upb_arena_addblock(a, mem, size, false);
+  }
+
+  if (alloc) {
+    a->block_alloc = alloc;
+  }
+}
+
+void upb_arena_uninit(upb_arena *a) {
+  cleanup_ent *ent = a->cleanup_head;
+  mem_block *block = a->block_head;
+
+  while (ent) {
+    ent->cleanup(ent->ud);
+    ent = ent->next;
+  }
+
+  /* Must do this after running cleanup functions, because this will delete
+   * the memory we store our cleanup entries in! */
+  while (block) {
+    mem_block *next = block->next;
+
+    if (block->owned) {
+      upb_free(a->block_alloc, block);
+    }
+
+    block = next;
+  }
+}
+
+bool upb_arena_addcleanup(upb_arena *a, upb_cleanup_func *func, void *ud) {
+  cleanup_ent *ent = upb_malloc(&a->alloc, sizeof(cleanup_ent));
+  if (!ent) {
+    return false;  /* Out of memory. */
+  }
+
+  ent->cleanup = func;
+  ent->ud = ud;
+  ent->next = a->cleanup_head;
+  a->cleanup_head = ent;
+
+  return true;
+}
+
+size_t upb_arena_bytesallocated(const upb_arena *a) {
+  return a->bytes_allocated;
+}
+
+
+/* Standard error functions ***************************************************/
+
+static bool default_err(void *ud, const upb_status *status) {
+  UPB_UNUSED(ud);
+  UPB_UNUSED(status);
+  return false;
+}
+
+static bool write_err_to(void *ud, const upb_status *status) {
+  upb_status *copy_to = ud;
+  upb_status_copy(copy_to, status);
+  return false;
+}
+
+
+/* upb_env ********************************************************************/
+
+void upb_env_initonly(upb_env *e) {
+  e->ok_ = true;
+  e->error_func_ = &default_err;
+  e->error_ud_ = NULL;
+}
+
+void upb_env_init(upb_env *e) {
+  upb_arena_init(&e->arena_);
+  upb_env_initonly(e);
+}
+
+void upb_env_init2(upb_env *e, void *mem, size_t n, upb_alloc *alloc) {
+  upb_arena_init2(&e->arena_, mem, n, alloc);
+  upb_env_initonly(e);
+}
+
+void upb_env_uninit(upb_env *e) {
+  upb_arena_uninit(&e->arena_);
+}
+
+void upb_env_seterrorfunc(upb_env *e, upb_error_func *func, void *ud) {
+  e->error_func_ = func;
+  e->error_ud_ = ud;
+}
+
+void upb_env_reporterrorsto(upb_env *e, upb_status *s) {
+  e->error_func_ = &write_err_to;
+  e->error_ud_ = s;
+}
+
+bool upb_env_reporterror(upb_env *e, const upb_status *status) {
+  e->ok_ = false;
+  return e->error_func_(e->error_ud_, status);
+}
+
+void *upb_env_malloc(upb_env *e, size_t size) {
+  return upb_malloc(&e->arena_.alloc, size);
+}
+
+void *upb_env_realloc(upb_env *e, void *ptr, size_t oldsize, size_t size) {
+  return upb_realloc(&e->arena_.alloc, ptr, oldsize, size);
+}
+
+void upb_env_free(upb_env *e, void *ptr) {
+  upb_free(&e->arena_.alloc, ptr);
+}
+
+bool upb_env_addcleanup(upb_env *e, upb_cleanup_func *func, void *ud) {
+  return upb_arena_addcleanup(&e->arena_, func, ud);
+}
+
+size_t upb_env_bytesallocated(const upb_env *e) {
+  return upb_arena_bytesallocated(&e->arena_);
+}
+/* This file was generated by upbc (the upb compiler) from the input
+ * file:
+ *
+ *     upb/descriptor/descriptor.proto
+ *
  * Do not edit -- your changes will be discarded when the file is
  * regenerated. */
 
 
-static const upb_msgdef msgs[20];
-static const upb_fielddef fields[81];
-static const upb_enumdef enums[4];
+static const upb_msgdef msgs[22];
+static const upb_fielddef fields[105];
+static const upb_enumdef enums[5];
 static const upb_tabent strentries[236];
-static const upb_tabent intentries[14];
-static const upb_tabval arrays[232];
+static const upb_tabent intentries[18];
+static const upb_tabval arrays[184];
 
 #ifdef UPB_DEBUG_REFS
-static upb_inttable reftables[212];
+static upb_inttable reftables[264];
 #endif
 
-static const upb_msgdef msgs[20] = {
-  UPB_MSGDEF_INIT("google.protobuf.DescriptorProto", 27, 6, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[0], 8, 7), UPB_STRTABLE_INIT(7, 15, UPB_CTYPE_PTR, 4, &strentries[0]),&reftables[0], &reftables[1]),
-  UPB_MSGDEF_INIT("google.protobuf.DescriptorProto.ExtensionRange", 4, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[8], 3, 2), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[16]),&reftables[2], &reftables[3]),
-  UPB_MSGDEF_INIT("google.protobuf.EnumDescriptorProto", 11, 2, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[11], 4, 3), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[20]),&reftables[4], &reftables[5]),
-  UPB_MSGDEF_INIT("google.protobuf.EnumOptions", 7, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[0], &arrays[15], 8, 1), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[24]),&reftables[6], &reftables[7]),
-  UPB_MSGDEF_INIT("google.protobuf.EnumValueDescriptorProto", 8, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[23], 4, 3), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[28]),&reftables[8], &reftables[9]),
-  UPB_MSGDEF_INIT("google.protobuf.EnumValueOptions", 6, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[2], &arrays[27], 4, 0), UPB_STRTABLE_INIT(1, 3, UPB_CTYPE_PTR, 2, &strentries[32]),&reftables[10], &reftables[11]),
-  UPB_MSGDEF_INIT("google.protobuf.FieldDescriptorProto", 19, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[31], 9, 8), UPB_STRTABLE_INIT(8, 15, UPB_CTYPE_PTR, 4, &strentries[36]),&reftables[12], &reftables[13]),
-  UPB_MSGDEF_INIT("google.protobuf.FieldOptions", 14, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[4], &arrays[40], 32, 6), UPB_STRTABLE_INIT(7, 15, UPB_CTYPE_PTR, 4, &strentries[52]),&reftables[14], &reftables[15]),
-  UPB_MSGDEF_INIT("google.protobuf.FileDescriptorProto", 39, 6, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[72], 12, 11), UPB_STRTABLE_INIT(11, 15, UPB_CTYPE_PTR, 4, &strentries[68]),&reftables[16], &reftables[17]),
-  UPB_MSGDEF_INIT("google.protobuf.FileDescriptorSet", 6, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[84], 2, 1), UPB_STRTABLE_INIT(1, 3, UPB_CTYPE_PTR, 2, &strentries[84]),&reftables[18], &reftables[19]),
-  UPB_MSGDEF_INIT("google.protobuf.FileOptions", 21, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[6], &arrays[86], 64, 9), UPB_STRTABLE_INIT(10, 15, UPB_CTYPE_PTR, 4, &strentries[88]),&reftables[20], &reftables[21]),
-  UPB_MSGDEF_INIT("google.protobuf.MessageOptions", 8, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[8], &arrays[150], 16, 2), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[104]),&reftables[22], &reftables[23]),
-  UPB_MSGDEF_INIT("google.protobuf.MethodDescriptorProto", 13, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[166], 5, 4), UPB_STRTABLE_INIT(4, 7, UPB_CTYPE_PTR, 3, &strentries[108]),&reftables[24], &reftables[25]),
-  UPB_MSGDEF_INIT("google.protobuf.MethodOptions", 6, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[10], &arrays[171], 4, 0), UPB_STRTABLE_INIT(1, 3, UPB_CTYPE_PTR, 2, &strentries[116]),&reftables[26], &reftables[27]),
-  UPB_MSGDEF_INIT("google.protobuf.ServiceDescriptorProto", 11, 2, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[175], 4, 3), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[120]),&reftables[28], &reftables[29]),
-  UPB_MSGDEF_INIT("google.protobuf.ServiceOptions", 6, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[12], &arrays[179], 4, 0), UPB_STRTABLE_INIT(1, 3, UPB_CTYPE_PTR, 2, &strentries[124]),&reftables[30], &reftables[31]),
-  UPB_MSGDEF_INIT("google.protobuf.SourceCodeInfo", 6, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[183], 2, 1), UPB_STRTABLE_INIT(1, 3, UPB_CTYPE_PTR, 2, &strentries[128]),&reftables[32], &reftables[33]),
-  UPB_MSGDEF_INIT("google.protobuf.SourceCodeInfo.Location", 14, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[185], 5, 4), UPB_STRTABLE_INIT(4, 7, UPB_CTYPE_PTR, 3, &strentries[132]),&reftables[34], &reftables[35]),
-  UPB_MSGDEF_INIT("google.protobuf.UninterpretedOption", 18, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[190], 9, 7), UPB_STRTABLE_INIT(7, 15, UPB_CTYPE_PTR, 4, &strentries[140]),&reftables[36], &reftables[37]),
-  UPB_MSGDEF_INIT("google.protobuf.UninterpretedOption.NamePart", 6, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[199], 3, 2), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[156]),&reftables[38], &reftables[39]),
+static const upb_msgdef msgs[22] = {
+  UPB_MSGDEF_INIT("google.protobuf.DescriptorProto", 40, 8, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[0], 11, 10), UPB_STRTABLE_INIT(10, 15, UPB_CTYPE_PTR, 4, &strentries[0]), false, UPB_SYNTAX_PROTO2, &reftables[0], &reftables[1]),
+  UPB_MSGDEF_INIT("google.protobuf.DescriptorProto.ExtensionRange", 4, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[11], 3, 2), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[16]), false, UPB_SYNTAX_PROTO2, &reftables[2], &reftables[3]),
+  UPB_MSGDEF_INIT("google.protobuf.DescriptorProto.ReservedRange", 4, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[14], 3, 2), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[20]), false, UPB_SYNTAX_PROTO2, &reftables[4], &reftables[5]),
+  UPB_MSGDEF_INIT("google.protobuf.EnumDescriptorProto", 11, 2, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[17], 4, 3), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[24]), false, UPB_SYNTAX_PROTO2, &reftables[6], &reftables[7]),
+  UPB_MSGDEF_INIT("google.protobuf.EnumOptions", 8, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[0], &arrays[21], 4, 2), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[28]), false, UPB_SYNTAX_PROTO2, &reftables[8], &reftables[9]),
+  UPB_MSGDEF_INIT("google.protobuf.EnumValueDescriptorProto", 8, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[25], 4, 3), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[32]), false, UPB_SYNTAX_PROTO2, &reftables[10], &reftables[11]),
+  UPB_MSGDEF_INIT("google.protobuf.EnumValueOptions", 7, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[2], &arrays[29], 2, 1), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[36]), false, UPB_SYNTAX_PROTO2, &reftables[12], &reftables[13]),
+  UPB_MSGDEF_INIT("google.protobuf.FieldDescriptorProto", 23, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[31], 11, 10), UPB_STRTABLE_INIT(10, 15, UPB_CTYPE_PTR, 4, &strentries[40]), false, UPB_SYNTAX_PROTO2, &reftables[14], &reftables[15]),
+  UPB_MSGDEF_INIT("google.protobuf.FieldOptions", 12, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[4], &arrays[42], 11, 6), UPB_STRTABLE_INIT(7, 15, UPB_CTYPE_PTR, 4, &strentries[56]), false, UPB_SYNTAX_PROTO2, &reftables[16], &reftables[17]),
+  UPB_MSGDEF_INIT("google.protobuf.FileDescriptorProto", 42, 6, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[53], 13, 12), UPB_STRTABLE_INIT(12, 15, UPB_CTYPE_PTR, 4, &strentries[72]), false, UPB_SYNTAX_PROTO2, &reftables[18], &reftables[19]),
+  UPB_MSGDEF_INIT("google.protobuf.FileDescriptorSet", 6, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[66], 2, 1), UPB_STRTABLE_INIT(1, 3, UPB_CTYPE_PTR, 2, &strentries[88]), false, UPB_SYNTAX_PROTO2, &reftables[20], &reftables[21]),
+  UPB_MSGDEF_INIT("google.protobuf.FileOptions", 31, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[6], &arrays[68], 39, 15), UPB_STRTABLE_INIT(16, 31, UPB_CTYPE_PTR, 5, &strentries[92]), false, UPB_SYNTAX_PROTO2, &reftables[22], &reftables[23]),
+  UPB_MSGDEF_INIT("google.protobuf.MessageOptions", 10, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[8], &arrays[107], 8, 4), UPB_STRTABLE_INIT(5, 7, UPB_CTYPE_PTR, 3, &strentries[124]), false, UPB_SYNTAX_PROTO2, &reftables[24], &reftables[25]),
+  UPB_MSGDEF_INIT("google.protobuf.MethodDescriptorProto", 15, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[115], 7, 6), UPB_STRTABLE_INIT(6, 7, UPB_CTYPE_PTR, 3, &strentries[132]), false, UPB_SYNTAX_PROTO2, &reftables[26], &reftables[27]),
+  UPB_MSGDEF_INIT("google.protobuf.MethodOptions", 7, 1, UPB_INTTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &intentries[10], &arrays[122], 1, 0), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[140]), false, UPB_SYNTAX_PROTO2, &reftables[28], &reftables[29]),
+  UPB_MSGDEF_INIT("google.protobuf.OneofDescriptorProto", 5, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[123], 2, 1), UPB_STRTABLE_INIT(1, 3, UPB_CTYPE_PTR, 2, &strentries[144]), false, UPB_SYNTAX_PROTO2, &reftables[30], &reftables[31]),
+  UPB_MSGDEF_INIT("google.protobuf.ServiceDescriptorProto", 11, 2, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[125], 4, 3), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[148]), false, UPB_SYNTAX_PROTO2, &reftables[32], &reftables[33]),
+  UPB_MSGDEF_INIT("google.protobuf.ServiceOptions", 7, 1, UPB_INTTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &intentries[14], &arrays[129], 1, 0), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[152]), false, UPB_SYNTAX_PROTO2, &reftables[34], &reftables[35]),
+  UPB_MSGDEF_INIT("google.protobuf.SourceCodeInfo", 6, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[130], 2, 1), UPB_STRTABLE_INIT(1, 3, UPB_CTYPE_PTR, 2, &strentries[156]), false, UPB_SYNTAX_PROTO2, &reftables[36], &reftables[37]),
+  UPB_MSGDEF_INIT("google.protobuf.SourceCodeInfo.Location", 19, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[132], 7, 5), UPB_STRTABLE_INIT(5, 7, UPB_CTYPE_PTR, 3, &strentries[160]), false, UPB_SYNTAX_PROTO2, &reftables[38], &reftables[39]),
+  UPB_MSGDEF_INIT("google.protobuf.UninterpretedOption", 18, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[139], 9, 7), UPB_STRTABLE_INIT(7, 15, UPB_CTYPE_PTR, 4, &strentries[168]), false, UPB_SYNTAX_PROTO2, &reftables[40], &reftables[41]),
+  UPB_MSGDEF_INIT("google.protobuf.UninterpretedOption.NamePart", 6, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[148], 3, 2), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[184]), false, UPB_SYNTAX_PROTO2, &reftables[42], &reftables[43]),
 };
 
-static const upb_fielddef fields[81] = {
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "aggregate_value", 8, &msgs[18], NULL, 15, 6, {0},&reftables[40], &reftables[41]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "allow_alias", 2, &msgs[3], NULL, 6, 1, {0},&reftables[42], &reftables[43]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "cc_generic_services", 16, &msgs[10], NULL, 17, 6, {0},&reftables[44], &reftables[45]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_ENUM, 0, false, false, false, false, "ctype", 1, &msgs[7], (const upb_def*)(&enums[2]), 6, 1, {0},&reftables[46], &reftables[47]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "default_value", 7, &msgs[6], NULL, 16, 7, {0},&reftables[48], &reftables[49]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_STRING, 0, false, false, false, false, "dependency", 3, &msgs[8], NULL, 30, 8, {0},&reftables[50], &reftables[51]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "deprecated", 3, &msgs[7], NULL, 8, 3, {0},&reftables[52], &reftables[53]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_DOUBLE, 0, false, false, false, false, "double_value", 6, &msgs[18], NULL, 11, 4, {0},&reftables[54], &reftables[55]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "end", 2, &msgs[1], NULL, 3, 1, {0},&reftables[56], &reftables[57]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "enum_type", 4, &msgs[0], (const upb_def*)(&msgs[2]), 16, 2, {0},&reftables[58], &reftables[59]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "enum_type", 5, &msgs[8], (const upb_def*)(&msgs[2]), 13, 1, {0},&reftables[60], &reftables[61]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "experimental_map_key", 9, &msgs[7], NULL, 10, 5, {0},&reftables[62], &reftables[63]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "extendee", 2, &msgs[6], NULL, 7, 2, {0},&reftables[64], &reftables[65]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "extension", 7, &msgs[8], (const upb_def*)(&msgs[6]), 19, 3, {0},&reftables[66], &reftables[67]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "extension", 6, &msgs[0], (const upb_def*)(&msgs[6]), 22, 4, {0},&reftables[68], &reftables[69]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "extension_range", 5, &msgs[0], (const upb_def*)(&msgs[1]), 19, 3, {0},&reftables[70], &reftables[71]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "field", 2, &msgs[0], (const upb_def*)(&msgs[6]), 10, 0, {0},&reftables[72], &reftables[73]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "file", 1, &msgs[9], (const upb_def*)(&msgs[8]), 5, 0, {0},&reftables[74], &reftables[75]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "go_package", 11, &msgs[10], NULL, 14, 5, {0},&reftables[76], &reftables[77]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "identifier_value", 3, &msgs[18], NULL, 6, 1, {0},&reftables[78], &reftables[79]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "input_type", 2, &msgs[12], NULL, 7, 2, {0},&reftables[80], &reftables[81]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_REQUIRED, UPB_TYPE_BOOL, 0, false, false, false, false, "is_extension", 2, &msgs[19], NULL, 5, 1, {0},&reftables[82], &reftables[83]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "java_generate_equals_and_hash", 20, &msgs[10], NULL, 20, 9, {0},&reftables[84], &reftables[85]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "java_generic_services", 17, &msgs[10], NULL, 18, 7, {0},&reftables[86], &reftables[87]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "java_multiple_files", 10, &msgs[10], NULL, 13, 4, {0},&reftables[88], &reftables[89]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "java_outer_classname", 8, &msgs[10], NULL, 9, 2, {0},&reftables[90], &reftables[91]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "java_package", 1, &msgs[10], NULL, 6, 1, {0},&reftables[92], &reftables[93]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_ENUM, 0, false, false, false, false, "label", 4, &msgs[6], (const upb_def*)(&enums[0]), 11, 4, {0},&reftables[94], &reftables[95]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "lazy", 5, &msgs[7], NULL, 9, 4, {0},&reftables[96], &reftables[97]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "leading_comments", 3, &msgs[17], NULL, 8, 2, {0},&reftables[98], &reftables[99]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "location", 1, &msgs[16], (const upb_def*)(&msgs[17]), 5, 0, {0},&reftables[100], &reftables[101]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "message_set_wire_format", 1, &msgs[11], NULL, 6, 1, {0},&reftables[102], &reftables[103]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "message_type", 4, &msgs[8], (const upb_def*)(&msgs[0]), 10, 0, {0},&reftables[104], &reftables[105]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "method", 2, &msgs[14], (const upb_def*)(&msgs[12]), 6, 0, {0},&reftables[106], &reftables[107]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[8], NULL, 22, 6, {0},&reftables[108], &reftables[109]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[14], NULL, 8, 2, {0},&reftables[110], &reftables[111]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "name", 2, &msgs[18], (const upb_def*)(&msgs[19]), 5, 0, {0},&reftables[112], &reftables[113]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[4], NULL, 4, 1, {0},&reftables[114], &reftables[115]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[0], NULL, 24, 6, {0},&reftables[116], &reftables[117]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[12], NULL, 4, 1, {0},&reftables[118], &reftables[119]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[2], NULL, 8, 2, {0},&reftables[120], &reftables[121]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[6], NULL, 4, 1, {0},&reftables[122], &reftables[123]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_REQUIRED, UPB_TYPE_STRING, 0, false, false, false, false, "name_part", 1, &msgs[19], NULL, 2, 0, {0},&reftables[124], &reftables[125]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT64, UPB_INTFMT_VARIABLE, false, false, false, false, "negative_int_value", 5, &msgs[18], NULL, 10, 3, {0},&reftables[126], &reftables[127]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "nested_type", 3, &msgs[0], (const upb_def*)(&msgs[0]), 13, 1, {0},&reftables[128], &reftables[129]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "no_standard_descriptor_accessor", 2, &msgs[11], NULL, 7, 2, {0},&reftables[130], &reftables[131]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "number", 3, &msgs[6], NULL, 10, 3, {0},&reftables[132], &reftables[133]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "number", 2, &msgs[4], NULL, 7, 2, {0},&reftables[134], &reftables[135]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_ENUM, 0, false, false, false, false, "optimize_for", 9, &msgs[10], (const upb_def*)(&enums[3]), 12, 3, {0},&reftables[136], &reftables[137]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 7, &msgs[0], (const upb_def*)(&msgs[11]), 23, 5, {0},&reftables[138], &reftables[139]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 3, &msgs[2], (const upb_def*)(&msgs[3]), 7, 1, {0},&reftables[140], &reftables[141]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 8, &msgs[6], (const upb_def*)(&msgs[7]), 3, 0, {0},&reftables[142], &reftables[143]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 3, &msgs[4], (const upb_def*)(&msgs[5]), 3, 0, {0},&reftables[144], &reftables[145]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 8, &msgs[8], (const upb_def*)(&msgs[10]), 20, 4, {0},&reftables[146], &reftables[147]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 3, &msgs[14], (const upb_def*)(&msgs[15]), 7, 1, {0},&reftables[148], &reftables[149]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 4, &msgs[12], (const upb_def*)(&msgs[13]), 3, 0, {0},&reftables[150], &reftables[151]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "output_type", 3, &msgs[12], NULL, 10, 3, {0},&reftables[152], &reftables[153]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "package", 2, &msgs[8], NULL, 25, 7, {0},&reftables[154], &reftables[155]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "packed", 2, &msgs[7], NULL, 7, 2, {0},&reftables[156], &reftables[157]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, true, "path", 1, &msgs[17], NULL, 4, 0, {0},&reftables[158], &reftables[159]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_UINT64, UPB_INTFMT_VARIABLE, false, false, false, false, "positive_int_value", 4, &msgs[18], NULL, 9, 2, {0},&reftables[160], &reftables[161]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "public_dependency", 10, &msgs[8], NULL, 35, 9, {0},&reftables[162], &reftables[163]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "py_generic_services", 18, &msgs[10], NULL, 19, 8, {0},&reftables[164], &reftables[165]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "service", 6, &msgs[8], (const upb_def*)(&msgs[14]), 16, 2, {0},&reftables[166], &reftables[167]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "source_code_info", 9, &msgs[8], (const upb_def*)(&msgs[16]), 21, 5, {0},&reftables[168], &reftables[169]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, true, "span", 2, &msgs[17], NULL, 7, 1, {0},&reftables[170], &reftables[171]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "start", 1, &msgs[1], NULL, 2, 0, {0},&reftables[172], &reftables[173]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BYTES, 0, false, false, false, false, "string_value", 7, &msgs[18], NULL, 12, 5, {0},&reftables[174], &reftables[175]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "trailing_comments", 4, &msgs[17], NULL, 11, 3, {0},&reftables[176], &reftables[177]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_ENUM, 0, false, false, false, false, "type", 5, &msgs[6], (const upb_def*)(&enums[1]), 12, 5, {0},&reftables[178], &reftables[179]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "type_name", 6, &msgs[6], NULL, 13, 6, {0},&reftables[180], &reftables[181]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[5], (const upb_def*)(&msgs[18]), 5, 0, {0},&reftables[182], &reftables[183]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[15], (const upb_def*)(&msgs[18]), 5, 0, {0},&reftables[184], &reftables[185]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[3], (const upb_def*)(&msgs[18]), 5, 0, {0},&reftables[186], &reftables[187]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[13], (const upb_def*)(&msgs[18]), 5, 0, {0},&reftables[188], &reftables[189]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[10], (const upb_def*)(&msgs[18]), 5, 0, {0},&reftables[190], &reftables[191]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[11], (const upb_def*)(&msgs[18]), 5, 0, {0},&reftables[192], &reftables[193]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[7], (const upb_def*)(&msgs[18]), 5, 0, {0},&reftables[194], &reftables[195]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "value", 2, &msgs[2], (const upb_def*)(&msgs[4]), 6, 0, {0},&reftables[196], &reftables[197]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "weak", 10, &msgs[7], NULL, 13, 6, {0},&reftables[198], &reftables[199]),
-  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "weak_dependency", 11, &msgs[8], NULL, 38, 10, {0},&reftables[200], &reftables[201]),
+static const upb_fielddef fields[105] = {
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "aggregate_value", 8, &msgs[20], NULL, 15, 6, {0},&reftables[44], &reftables[45]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "allow_alias", 2, &msgs[4], NULL, 6, 1, {0},&reftables[46], &reftables[47]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "cc_enable_arenas", 31, &msgs[11], NULL, 23, 12, {0},&reftables[48], &reftables[49]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "cc_generic_services", 16, &msgs[11], NULL, 17, 6, {0},&reftables[50], &reftables[51]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "client_streaming", 5, &msgs[13], NULL, 13, 4, {0},&reftables[52], &reftables[53]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "csharp_namespace", 37, &msgs[11], NULL, 27, 14, {0},&reftables[54], &reftables[55]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_ENUM, 0, false, false, false, false, "ctype", 1, &msgs[8], (const upb_def*)(&enums[2]), 6, 1, {0},&reftables[56], &reftables[57]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "default_value", 7, &msgs[7], NULL, 16, 7, {0},&reftables[58], &reftables[59]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_STRING, 0, false, false, false, false, "dependency", 3, &msgs[9], NULL, 30, 8, {0},&reftables[60], &reftables[61]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "deprecated", 3, &msgs[12], NULL, 8, 3, {0},&reftables[62], &reftables[63]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "deprecated", 3, &msgs[8], NULL, 8, 3, {0},&reftables[64], &reftables[65]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "deprecated", 33, &msgs[14], NULL, 6, 1, {0},&reftables[66], &reftables[67]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "deprecated", 23, &msgs[11], NULL, 21, 10, {0},&reftables[68], &reftables[69]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "deprecated", 3, &msgs[4], NULL, 7, 2, {0},&reftables[70], &reftables[71]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "deprecated", 33, &msgs[17], NULL, 6, 1, {0},&reftables[72], &reftables[73]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "deprecated", 1, &msgs[6], NULL, 6, 1, {0},&reftables[74], &reftables[75]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_DOUBLE, 0, false, false, false, false, "double_value", 6, &msgs[20], NULL, 11, 4, {0},&reftables[76], &reftables[77]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "end", 2, &msgs[2], NULL, 3, 1, {0},&reftables[78], &reftables[79]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "end", 2, &msgs[1], NULL, 3, 1, {0},&reftables[80], &reftables[81]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "enum_type", 4, &msgs[0], (const upb_def*)(&msgs[3]), 18, 2, {0},&reftables[82], &reftables[83]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "enum_type", 5, &msgs[9], (const upb_def*)(&msgs[3]), 13, 1, {0},&reftables[84], &reftables[85]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "extendee", 2, &msgs[7], NULL, 7, 2, {0},&reftables[86], &reftables[87]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "extension", 6, &msgs[0], (const upb_def*)(&msgs[7]), 24, 4, {0},&reftables[88], &reftables[89]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "extension", 7, &msgs[9], (const upb_def*)(&msgs[7]), 19, 3, {0},&reftables[90], &reftables[91]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "extension_range", 5, &msgs[0], (const upb_def*)(&msgs[1]), 21, 3, {0},&reftables[92], &reftables[93]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "field", 2, &msgs[0], (const upb_def*)(&msgs[7]), 12, 0, {0},&reftables[94], &reftables[95]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "file", 1, &msgs[10], (const upb_def*)(&msgs[9]), 5, 0, {0},&reftables[96], &reftables[97]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "go_package", 11, &msgs[11], NULL, 14, 5, {0},&reftables[98], &reftables[99]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "identifier_value", 3, &msgs[20], NULL, 6, 1, {0},&reftables[100], &reftables[101]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "input_type", 2, &msgs[13], NULL, 7, 2, {0},&reftables[102], &reftables[103]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_REQUIRED, UPB_TYPE_BOOL, 0, false, false, false, false, "is_extension", 2, &msgs[21], NULL, 5, 1, {0},&reftables[104], &reftables[105]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "java_generate_equals_and_hash", 20, &msgs[11], NULL, 20, 9, {0},&reftables[106], &reftables[107]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "java_generic_services", 17, &msgs[11], NULL, 18, 7, {0},&reftables[108], &reftables[109]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "java_multiple_files", 10, &msgs[11], NULL, 13, 4, {0},&reftables[110], &reftables[111]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "java_outer_classname", 8, &msgs[11], NULL, 9, 2, {0},&reftables[112], &reftables[113]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "java_package", 1, &msgs[11], NULL, 6, 1, {0},&reftables[114], &reftables[115]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "java_string_check_utf8", 27, &msgs[11], NULL, 22, 11, {0},&reftables[116], &reftables[117]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "javanano_use_deprecated_package", 38, &msgs[11], NULL, 30, 15, {0},&reftables[118], &reftables[119]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "json_name", 10, &msgs[7], NULL, 20, 9, {0},&reftables[120], &reftables[121]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_ENUM, 0, false, false, false, false, "jstype", 6, &msgs[8], (const upb_def*)(&enums[3]), 10, 5, {0},&reftables[122], &reftables[123]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_ENUM, 0, false, false, false, false, "label", 4, &msgs[7], (const upb_def*)(&enums[0]), 11, 4, {0},&reftables[124], &reftables[125]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "lazy", 5, &msgs[8], NULL, 9, 4, {0},&reftables[126], &reftables[127]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "leading_comments", 3, &msgs[19], NULL, 8, 2, {0},&reftables[128], &reftables[129]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_STRING, 0, false, false, false, false, "leading_detached_comments", 6, &msgs[19], NULL, 16, 4, {0},&reftables[130], &reftables[131]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "location", 1, &msgs[18], (const upb_def*)(&msgs[19]), 5, 0, {0},&reftables[132], &reftables[133]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "map_entry", 7, &msgs[12], NULL, 9, 4, {0},&reftables[134], &reftables[135]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "message_set_wire_format", 1, &msgs[12], NULL, 6, 1, {0},&reftables[136], &reftables[137]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "message_type", 4, &msgs[9], (const upb_def*)(&msgs[0]), 10, 0, {0},&reftables[138], &reftables[139]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "method", 2, &msgs[16], (const upb_def*)(&msgs[13]), 6, 0, {0},&reftables[140], &reftables[141]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[3], NULL, 8, 2, {0},&reftables[142], &reftables[143]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[15], NULL, 2, 0, {0},&reftables[144], &reftables[145]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "name", 2, &msgs[20], (const upb_def*)(&msgs[21]), 5, 0, {0},&reftables[146], &reftables[147]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[0], NULL, 32, 8, {0},&reftables[148], &reftables[149]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[5], NULL, 4, 1, {0},&reftables[150], &reftables[151]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[9], NULL, 22, 6, {0},&reftables[152], &reftables[153]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[7], NULL, 4, 1, {0},&reftables[154], &reftables[155]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[13], NULL, 4, 1, {0},&reftables[156], &reftables[157]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[16], NULL, 8, 2, {0},&reftables[158], &reftables[159]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_REQUIRED, UPB_TYPE_STRING, 0, false, false, false, false, "name_part", 1, &msgs[21], NULL, 2, 0, {0},&reftables[160], &reftables[161]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT64, UPB_INTFMT_VARIABLE, false, false, false, false, "negative_int_value", 5, &msgs[20], NULL, 10, 3, {0},&reftables[162], &reftables[163]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "nested_type", 3, &msgs[0], (const upb_def*)(&msgs[0]), 15, 1, {0},&reftables[164], &reftables[165]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "no_standard_descriptor_accessor", 2, &msgs[12], NULL, 7, 2, {0},&reftables[166], &reftables[167]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "number", 2, &msgs[5], NULL, 7, 2, {0},&reftables[168], &reftables[169]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "number", 3, &msgs[7], NULL, 10, 3, {0},&reftables[170], &reftables[171]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "objc_class_prefix", 36, &msgs[11], NULL, 24, 13, {0},&reftables[172], &reftables[173]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "oneof_decl", 8, &msgs[0], (const upb_def*)(&msgs[15]), 28, 6, {0},&reftables[174], &reftables[175]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "oneof_index", 9, &msgs[7], NULL, 19, 8, {0},&reftables[176], &reftables[177]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_ENUM, 0, false, false, false, false, "optimize_for", 9, &msgs[11], (const upb_def*)(&enums[4]), 12, 3, {0},&reftables[178], &reftables[179]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 7, &msgs[0], (const upb_def*)(&msgs[12]), 25, 5, {0},&reftables[180], &reftables[181]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 8, &msgs[9], (const upb_def*)(&msgs[11]), 20, 4, {0},&reftables[182], &reftables[183]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 4, &msgs[13], (const upb_def*)(&msgs[14]), 3, 0, {0},&reftables[184], &reftables[185]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 8, &msgs[7], (const upb_def*)(&msgs[8]), 3, 0, {0},&reftables[186], &reftables[187]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 3, &msgs[16], (const upb_def*)(&msgs[17]), 7, 1, {0},&reftables[188], &reftables[189]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 3, &msgs[5], (const upb_def*)(&msgs[6]), 3, 0, {0},&reftables[190], &reftables[191]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 3, &msgs[3], (const upb_def*)(&msgs[4]), 7, 1, {0},&reftables[192], &reftables[193]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "output_type", 3, &msgs[13], NULL, 10, 3, {0},&reftables[194], &reftables[195]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "package", 2, &msgs[9], NULL, 25, 7, {0},&reftables[196], &reftables[197]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "packed", 2, &msgs[8], NULL, 7, 2, {0},&reftables[198], &reftables[199]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, true, "path", 1, &msgs[19], NULL, 4, 0, {0},&reftables[200], &reftables[201]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_UINT64, UPB_INTFMT_VARIABLE, false, false, false, false, "positive_int_value", 4, &msgs[20], NULL, 9, 2, {0},&reftables[202], &reftables[203]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "public_dependency", 10, &msgs[9], NULL, 35, 9, {0},&reftables[204], &reftables[205]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "py_generic_services", 18, &msgs[11], NULL, 19, 8, {0},&reftables[206], &reftables[207]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_STRING, 0, false, false, false, false, "reserved_name", 10, &msgs[0], NULL, 37, 9, {0},&reftables[208], &reftables[209]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "reserved_range", 9, &msgs[0], (const upb_def*)(&msgs[2]), 31, 7, {0},&reftables[210], &reftables[211]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "server_streaming", 6, &msgs[13], NULL, 14, 5, {0},&reftables[212], &reftables[213]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "service", 6, &msgs[9], (const upb_def*)(&msgs[16]), 16, 2, {0},&reftables[214], &reftables[215]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "source_code_info", 9, &msgs[9], (const upb_def*)(&msgs[18]), 21, 5, {0},&reftables[216], &reftables[217]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, true, "span", 2, &msgs[19], NULL, 7, 1, {0},&reftables[218], &reftables[219]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "start", 1, &msgs[2], NULL, 2, 0, {0},&reftables[220], &reftables[221]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "start", 1, &msgs[1], NULL, 2, 0, {0},&reftables[222], &reftables[223]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BYTES, 0, false, false, false, false, "string_value", 7, &msgs[20], NULL, 12, 5, {0},&reftables[224], &reftables[225]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "syntax", 12, &msgs[9], NULL, 39, 11, {0},&reftables[226], &reftables[227]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "trailing_comments", 4, &msgs[19], NULL, 11, 3, {0},&reftables[228], &reftables[229]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_ENUM, 0, false, false, false, false, "type", 5, &msgs[7], (const upb_def*)(&enums[1]), 12, 5, {0},&reftables[230], &reftables[231]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "type_name", 6, &msgs[7], NULL, 13, 6, {0},&reftables[232], &reftables[233]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[11], (const upb_def*)(&msgs[20]), 5, 0, {0},&reftables[234], &reftables[235]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[12], (const upb_def*)(&msgs[20]), 5, 0, {0},&reftables[236], &reftables[237]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[6], (const upb_def*)(&msgs[20]), 5, 0, {0},&reftables[238], &reftables[239]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[4], (const upb_def*)(&msgs[20]), 5, 0, {0},&reftables[240], &reftables[241]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[8], (const upb_def*)(&msgs[20]), 5, 0, {0},&reftables[242], &reftables[243]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[14], (const upb_def*)(&msgs[20]), 5, 0, {0},&reftables[244], &reftables[245]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[17], (const upb_def*)(&msgs[20]), 5, 0, {0},&reftables[246], &reftables[247]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "value", 2, &msgs[3], (const upb_def*)(&msgs[5]), 6, 0, {0},&reftables[248], &reftables[249]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "weak", 10, &msgs[8], NULL, 11, 6, {0},&reftables[250], &reftables[251]),
+  UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "weak_dependency", 11, &msgs[9], NULL, 38, 10, {0},&reftables[252], &reftables[253]),
 };
 
-static const upb_enumdef enums[4] = {
-  UPB_ENUMDEF_INIT("google.protobuf.FieldDescriptorProto.Label", UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_INT32, 2, &strentries[160]), UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_CSTR, 0, NULL, &arrays[202], 4, 3), 0, &reftables[202], &reftables[203]),
-  UPB_ENUMDEF_INIT("google.protobuf.FieldDescriptorProto.Type", UPB_STRTABLE_INIT(18, 31, UPB_CTYPE_INT32, 5, &strentries[164]), UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_CSTR, 0, NULL, &arrays[206], 19, 18), 0, &reftables[204], &reftables[205]),
-  UPB_ENUMDEF_INIT("google.protobuf.FieldOptions.CType", UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_INT32, 2, &strentries[196]), UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_CSTR, 0, NULL, &arrays[225], 3, 3), 0, &reftables[206], &reftables[207]),
-  UPB_ENUMDEF_INIT("google.protobuf.FileOptions.OptimizeMode", UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_INT32, 2, &strentries[200]), UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_CSTR, 0, NULL, &arrays[228], 4, 3), 0, &reftables[208], &reftables[209]),
+static const upb_enumdef enums[5] = {
+  UPB_ENUMDEF_INIT("google.protobuf.FieldDescriptorProto.Label", UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_INT32, 2, &strentries[188]), UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_CSTR, 0, NULL, &arrays[151], 4, 3), 0, &reftables[254], &reftables[255]),
+  UPB_ENUMDEF_INIT("google.protobuf.FieldDescriptorProto.Type", UPB_STRTABLE_INIT(18, 31, UPB_CTYPE_INT32, 5, &strentries[192]), UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_CSTR, 0, NULL, &arrays[155], 19, 18), 0, &reftables[256], &reftables[257]),
+  UPB_ENUMDEF_INIT("google.protobuf.FieldOptions.CType", UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_INT32, 2, &strentries[224]), UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_CSTR, 0, NULL, &arrays[174], 3, 3), 0, &reftables[258], &reftables[259]),
+  UPB_ENUMDEF_INIT("google.protobuf.FieldOptions.JSType", UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_INT32, 2, &strentries[228]), UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_CSTR, 0, NULL, &arrays[177], 3, 3), 0, &reftables[260], &reftables[261]),
+  UPB_ENUMDEF_INIT("google.protobuf.FileOptions.OptimizeMode", UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_INT32, 2, &strentries[232]), UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_CSTR, 0, NULL, &arrays[180], 4, 3), 0, &reftables[262], &reftables[263]),
 };
 
 static const upb_tabent strentries[236] = {
-  {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "extension"), UPB_TABVALUE_PTR_INIT(&fields[14]), NULL},
+  {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "extension"), UPB_TABVALUE_PTR_INIT(&fields[22]), NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[38]), NULL},
+  {UPB_TABKEY_STR("\015", "\000", "\000", "\000", "reserved_name"), UPB_TABVALUE_PTR_INIT(&fields[82]), NULL},
+  {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[52]), NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\005", "\000", "\000", "\000", "field"), UPB_TABVALUE_PTR_INIT(&fields[16]), NULL},
-  {UPB_TABKEY_STR("\017", "\000", "\000", "\000", "extension_range"), UPB_TABVALUE_PTR_INIT(&fields[15]), NULL},
+  {UPB_TABKEY_STR("\005", "\000", "\000", "\000", "field"), UPB_TABVALUE_PTR_INIT(&fields[25]), &strentries[12]},
+  {UPB_TABKEY_STR("\017", "\000", "\000", "\000", "extension_range"), UPB_TABVALUE_PTR_INIT(&fields[24]), &strentries[14]},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "nested_type"), UPB_TABVALUE_PTR_INIT(&fields[44]), NULL},
+  {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "nested_type"), UPB_TABVALUE_PTR_INIT(&fields[60]), NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_STR("\016", "\000", "\000", "\000", "reserved_range"), UPB_TABVALUE_PTR_INIT(&fields[83]), NULL},
+  {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[68]), NULL},
+  {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "oneof_decl"), UPB_TABVALUE_PTR_INIT(&fields[65]), NULL},
+  {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "enum_type"), UPB_TABVALUE_PTR_INIT(&fields[19]), &strentries[13]},
+  {UPB_TABKEY_STR("\005", "\000", "\000", "\000", "start"), UPB_TABVALUE_PTR_INIT(&fields[89]), NULL},
+  {UPB_TABKEY_STR("\003", "\000", "\000", "\000", "end"), UPB_TABVALUE_PTR_INIT(&fields[18]), NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_STR("\005", "\000", "\000", "\000", "start"), UPB_TABVALUE_PTR_INIT(&fields[88]), NULL},
+  {UPB_TABKEY_STR("\003", "\000", "\000", "\000", "end"), UPB_TABVALUE_PTR_INIT(&fields[17]), NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[49]), NULL},
-  {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "enum_type"), UPB_TABVALUE_PTR_INIT(&fields[9]), &strentries[14]},
-  {UPB_TABKEY_STR("\005", "\000", "\000", "\000", "start"), UPB_TABVALUE_PTR_INIT(&fields[66]), NULL},
-  {UPB_TABKEY_STR("\003", "\000", "\000", "\000", "end"), UPB_TABVALUE_PTR_INIT(&fields[8]), NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\005", "\000", "\000", "\000", "value"), UPB_TABVALUE_PTR_INIT(&fields[78]), NULL},
-  {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[50]), NULL},
-  {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[40]), &strentries[22]},
-  {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[73]), NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_STR("\005", "\000", "\000", "\000", "value"), UPB_TABVALUE_PTR_INIT(&fields[102]), NULL},
+  {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[74]), NULL},
+  {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[49]), &strentries[26]},
+  {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[98]), NULL},
+  {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "deprecated"), UPB_TABVALUE_PTR_INIT(&fields[13]), NULL},
   {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "allow_alias"), UPB_TABVALUE_PTR_INIT(&fields[1]), NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\006", "\000", "\000", "\000", "number"), UPB_TABVALUE_PTR_INIT(&fields[47]), NULL},
+  {UPB_TABKEY_STR("\006", "\000", "\000", "\000", "number"), UPB_TABVALUE_PTR_INIT(&fields[62]), NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[52]), NULL},
-  {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[37]), &strentries[30]},
-  {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[71]), NULL},
+  {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[73]), NULL},
+  {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[53]), &strentries[34]},
+  {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[97]), NULL},
+  {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "deprecated"), UPB_TABVALUE_PTR_INIT(&fields[15]), NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "oneof_index"), UPB_TABVALUE_PTR_INIT(&fields[66]), NULL},
+  {UPB_TABKEY_STR("\005", "\000", "\000", "\000", "label"), UPB_TABVALUE_PTR_INIT(&fields[40]), NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[55]), NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\005", "\000", "\000", "\000", "label"), UPB_TABVALUE_PTR_INIT(&fields[27]), NULL},
+  {UPB_TABKEY_STR("\006", "\000", "\000", "\000", "number"), UPB_TABVALUE_PTR_INIT(&fields[63]), &strentries[53]},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[41]), NULL},
+  {UPB_TABKEY_STR("\010", "\000", "\000", "\000", "extendee"), UPB_TABVALUE_PTR_INIT(&fields[21]), NULL},
+  {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "type_name"), UPB_TABVALUE_PTR_INIT(&fields[94]), NULL},
+  {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "json_name"), UPB_TABVALUE_PTR_INIT(&fields[38]), NULL},
+  {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "type"), UPB_TABVALUE_PTR_INIT(&fields[93]), &strentries[50]},
+  {UPB_TABKEY_STR("\015", "\000", "\000", "\000", "default_value"), UPB_TABVALUE_PTR_INIT(&fields[7]), NULL},
+  {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[71]), NULL},
+  {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[99]), NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "weak"), UPB_TABVALUE_PTR_INIT(&fields[103]), NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\006", "\000", "\000", "\000", "number"), UPB_TABVALUE_PTR_INIT(&fields[46]), &strentries[49]},
+  {UPB_TABKEY_STR("\006", "\000", "\000", "\000", "packed"), UPB_TABVALUE_PTR_INIT(&fields[77]), NULL},
+  {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "lazy"), UPB_TABVALUE_PTR_INIT(&fields[41]), NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_STR("\005", "\000", "\000", "\000", "ctype"), UPB_TABVALUE_PTR_INIT(&fields[6]), NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_STR("\006", "\000", "\000", "\000", "jstype"), UPB_TABVALUE_PTR_INIT(&fields[39]), NULL},
+  {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "deprecated"), UPB_TABVALUE_PTR_INIT(&fields[10]), NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "type_name"), UPB_TABVALUE_PTR_INIT(&fields[70]), NULL},
-  {UPB_TABKEY_STR("\010", "\000", "\000", "\000", "extendee"), UPB_TABVALUE_PTR_INIT(&fields[12]), NULL},
-  {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "type"), UPB_TABVALUE_PTR_INIT(&fields[69]), &strentries[48]},
-  {UPB_TABKEY_STR("\015", "\000", "\000", "\000", "default_value"), UPB_TABVALUE_PTR_INIT(&fields[4]), NULL},
-  {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[51]), NULL},
-  {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "experimental_map_key"), UPB_TABVALUE_PTR_INIT(&fields[11]), &strentries[67]},
+  {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "extension"), UPB_TABVALUE_PTR_INIT(&fields[23]), NULL},
+  {UPB_TABKEY_STR("\017", "\000", "\000", "\000", "weak_dependency"), UPB_TABVALUE_PTR_INIT(&fields[104]), NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "weak"), UPB_TABVALUE_PTR_INIT(&fields[79]), NULL},
+  {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[54]), NULL},
+  {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "service"), UPB_TABVALUE_PTR_INIT(&fields[85]), NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_STR("\020", "\000", "\000", "\000", "source_code_info"), UPB_TABVALUE_PTR_INIT(&fields[86]), NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_STR("\006", "\000", "\000", "\000", "syntax"), UPB_TABVALUE_PTR_INIT(&fields[91]), NULL},
+  {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "dependency"), UPB_TABVALUE_PTR_INIT(&fields[8]), NULL},
+  {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "message_type"), UPB_TABVALUE_PTR_INIT(&fields[47]), NULL},
+  {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "package"), UPB_TABVALUE_PTR_INIT(&fields[76]), NULL},
+  {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[69]), &strentries[86]},
+  {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "enum_type"), UPB_TABVALUE_PTR_INIT(&fields[20]), NULL},
+  {UPB_TABKEY_STR("\021", "\000", "\000", "\000", "public_dependency"), UPB_TABVALUE_PTR_INIT(&fields[80]), &strentries[85]},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "file"), UPB_TABVALUE_PTR_INIT(&fields[26]), NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\006", "\000", "\000", "\000", "packed"), UPB_TABVALUE_PTR_INIT(&fields[58]), NULL},
-  {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "lazy"), UPB_TABVALUE_PTR_INIT(&fields[28]), NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\005", "\000", "\000", "\000", "ctype"), UPB_TABVALUE_PTR_INIT(&fields[3]), NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "deprecated"), UPB_TABVALUE_PTR_INIT(&fields[6]), NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[77]), NULL},
-  {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "extension"), UPB_TABVALUE_PTR_INIT(&fields[13]), NULL},
-  {UPB_TABKEY_STR("\017", "\000", "\000", "\000", "weak_dependency"), UPB_TABVALUE_PTR_INIT(&fields[80]), NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[34]), NULL},
-  {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "service"), UPB_TABVALUE_PTR_INIT(&fields[63]), NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\020", "\000", "\000", "\000", "source_code_info"), UPB_TABVALUE_PTR_INIT(&fields[64]), NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "dependency"), UPB_TABVALUE_PTR_INIT(&fields[5]), NULL},
-  {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "message_type"), UPB_TABVALUE_PTR_INIT(&fields[32]), NULL},
-  {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "package"), UPB_TABVALUE_PTR_INIT(&fields[57]), NULL},
-  {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[53]), &strentries[82]},
-  {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "enum_type"), UPB_TABVALUE_PTR_INIT(&fields[10]), NULL},
-  {UPB_TABKEY_STR("\021", "\000", "\000", "\000", "public_dependency"), UPB_TABVALUE_PTR_INIT(&fields[61]), &strentries[81]},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "file"), UPB_TABVALUE_PTR_INIT(&fields[17]), NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[75]), NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\023", "\000", "\000", "\000", "cc_generic_services"), UPB_TABVALUE_PTR_INIT(&fields[2]), NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\023", "\000", "\000", "\000", "java_multiple_files"), UPB_TABVALUE_PTR_INIT(&fields[24]), NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\025", "\000", "\000", "\000", "java_generic_services"), UPB_TABVALUE_PTR_INIT(&fields[23]), &strentries[102]},
-  {UPB_TABKEY_STR("\035", "\000", "\000", "\000", "java_generate_equals_and_hash"), UPB_TABVALUE_PTR_INIT(&fields[22]), NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "go_package"), UPB_TABVALUE_PTR_INIT(&fields[18]), NULL},
-  {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "java_package"), UPB_TABVALUE_PTR_INIT(&fields[26]), NULL},
-  {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "optimize_for"), UPB_TABVALUE_PTR_INIT(&fields[48]), NULL},
-  {UPB_TABKEY_STR("\023", "\000", "\000", "\000", "py_generic_services"), UPB_TABVALUE_PTR_INIT(&fields[62]), NULL},
-  {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "java_outer_classname"), UPB_TABVALUE_PTR_INIT(&fields[25]), NULL},
-  {UPB_TABKEY_STR("\027", "\000", "\000", "\000", "message_set_wire_format"), UPB_TABVALUE_PTR_INIT(&fields[31]), &strentries[106]},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[76]), NULL},
-  {UPB_TABKEY_STR("\037", "\000", "\000", "\000", "no_standard_descriptor_accessor"), UPB_TABVALUE_PTR_INIT(&fields[45]), NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[39]), NULL},
-  {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "input_type"), UPB_TABVALUE_PTR_INIT(&fields[20]), NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "output_type"), UPB_TABVALUE_PTR_INIT(&fields[56]), NULL},
-  {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[55]), NULL},
-  {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[74]), NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[54]), &strentries[122]},
-  {UPB_TABKEY_STR("\006", "\000", "\000", "\000", "method"), UPB_TABVALUE_PTR_INIT(&fields[33]), NULL},
-  {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[35]), &strentries[121]},
-  {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[72]), NULL},
+  {UPB_TABKEY_STR("\023", "\000", "\000", "\000", "cc_generic_services"), UPB_TABVALUE_PTR_INIT(&fields[3]), NULL},
+  {UPB_TABKEY_STR("\020", "\000", "\000", "\000", "csharp_namespace"), UPB_TABVALUE_PTR_INIT(&fields[5]), NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\010", "\000", "\000", "\000", "location"), UPB_TABVALUE_PTR_INIT(&fields[30]), NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "go_package"), UPB_TABVALUE_PTR_INIT(&fields[27]), NULL},
+  {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "java_package"), UPB_TABVALUE_PTR_INIT(&fields[35]), &strentries[120]},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "java_outer_classname"), UPB_TABVALUE_PTR_INIT(&fields[34]), NULL},
+  {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[95]), NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_STR("\023", "\000", "\000", "\000", "java_multiple_files"), UPB_TABVALUE_PTR_INIT(&fields[33]), &strentries[117]},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_STR("\025", "\000", "\000", "\000", "java_generic_services"), UPB_TABVALUE_PTR_INIT(&fields[32]), &strentries[118]},
+  {UPB_TABKEY_STR("\035", "\000", "\000", "\000", "java_generate_equals_and_hash"), UPB_TABVALUE_PTR_INIT(&fields[31]), NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_STR("\037", "\000", "\000", "\000", "javanano_use_deprecated_package"), UPB_TABVALUE_PTR_INIT(&fields[37]), &strentries[123]},
+  {UPB_TABKEY_STR("\023", "\000", "\000", "\000", "py_generic_services"), UPB_TABVALUE_PTR_INIT(&fields[81]), NULL},
+  {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "optimize_for"), UPB_TABVALUE_PTR_INIT(&fields[67]), NULL},
+  {UPB_TABKEY_STR("\026", "\000", "\000", "\000", "java_string_check_utf8"), UPB_TABVALUE_PTR_INIT(&fields[36]), NULL},
+  {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "deprecated"), UPB_TABVALUE_PTR_INIT(&fields[12]), &strentries[119]},
+  {UPB_TABKEY_STR("\021", "\000", "\000", "\000", "objc_class_prefix"), UPB_TABVALUE_PTR_INIT(&fields[64]), NULL},
+  {UPB_TABKEY_STR("\020", "\000", "\000", "\000", "cc_enable_arenas"), UPB_TABVALUE_PTR_INIT(&fields[2]), NULL},
+  {UPB_TABKEY_STR("\027", "\000", "\000", "\000", "message_set_wire_format"), UPB_TABVALUE_PTR_INIT(&fields[46]), &strentries[128]},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[96]), NULL},
+  {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "deprecated"), UPB_TABVALUE_PTR_INIT(&fields[9]), NULL},
+  {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "map_entry"), UPB_TABVALUE_PTR_INIT(&fields[45]), NULL},
+  {UPB_TABKEY_STR("\037", "\000", "\000", "\000", "no_standard_descriptor_accessor"), UPB_TABVALUE_PTR_INIT(&fields[61]), NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_STR("\020", "\000", "\000", "\000", "client_streaming"), UPB_TABVALUE_PTR_INIT(&fields[4]), NULL},
+  {UPB_TABKEY_STR("\020", "\000", "\000", "\000", "server_streaming"), UPB_TABVALUE_PTR_INIT(&fields[84]), NULL},
+  {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[56]), NULL},
+  {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "input_type"), UPB_TABVALUE_PTR_INIT(&fields[29]), NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "output_type"), UPB_TABVALUE_PTR_INIT(&fields[75]), NULL},
+  {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[70]), NULL},
+  {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[100]), NULL},
+  {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "deprecated"), UPB_TABVALUE_PTR_INIT(&fields[11]), NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "span"), UPB_TABVALUE_PTR_INIT(&fields[65]), &strentries[139]},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\021", "\000", "\000", "\000", "trailing_comments"), UPB_TABVALUE_PTR_INIT(&fields[68]), NULL},
-  {UPB_TABKEY_STR("\020", "\000", "\000", "\000", "leading_comments"), UPB_TABVALUE_PTR_INIT(&fields[29]), &strentries[137]},
-  {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "path"), UPB_TABVALUE_PTR_INIT(&fields[59]), NULL},
-  {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "double_value"), UPB_TABVALUE_PTR_INIT(&fields[7]), NULL},
+  {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[50]), NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[36]), NULL},
+  {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[72]), &strentries[150]},
+  {UPB_TABKEY_STR("\006", "\000", "\000", "\000", "method"), UPB_TABVALUE_PTR_INIT(&fields[48]), NULL},
+  {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[57]), &strentries[149]},
+  {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[101]), NULL},
+  {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "deprecated"), UPB_TABVALUE_PTR_INIT(&fields[14]), NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\022", "\000", "\000", "\000", "negative_int_value"), UPB_TABVALUE_PTR_INIT(&fields[43]), NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_STR("\010", "\000", "\000", "\000", "location"), UPB_TABVALUE_PTR_INIT(&fields[44]), NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "span"), UPB_TABVALUE_PTR_INIT(&fields[87]), &strentries[167]},
+  {UPB_TABKEY_STR("\031", "\000", "\000", "\000", "leading_detached_comments"), UPB_TABVALUE_PTR_INIT(&fields[43]), &strentries[165]},
+  {UPB_TABKEY_STR("\021", "\000", "\000", "\000", "trailing_comments"), UPB_TABVALUE_PTR_INIT(&fields[92]), NULL},
+  {UPB_TABKEY_STR("\020", "\000", "\000", "\000", "leading_comments"), UPB_TABVALUE_PTR_INIT(&fields[42]), &strentries[164]},
+  {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "path"), UPB_TABVALUE_PTR_INIT(&fields[78]), NULL},
+  {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "double_value"), UPB_TABVALUE_PTR_INIT(&fields[16]), NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[51]), NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_STR("\022", "\000", "\000", "\000", "negative_int_value"), UPB_TABVALUE_PTR_INIT(&fields[59]), NULL},
   {UPB_TABKEY_STR("\017", "\000", "\000", "\000", "aggregate_value"), UPB_TABVALUE_PTR_INIT(&fields[0]), NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\022", "\000", "\000", "\000", "positive_int_value"), UPB_TABVALUE_PTR_INIT(&fields[60]), NULL},
-  {UPB_TABKEY_STR("\020", "\000", "\000", "\000", "identifier_value"), UPB_TABVALUE_PTR_INIT(&fields[19]), NULL},
-  {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "string_value"), UPB_TABVALUE_PTR_INIT(&fields[67]), &strentries[154]},
+  {UPB_TABKEY_STR("\022", "\000", "\000", "\000", "positive_int_value"), UPB_TABVALUE_PTR_INIT(&fields[79]), NULL},
+  {UPB_TABKEY_STR("\020", "\000", "\000", "\000", "identifier_value"), UPB_TABVALUE_PTR_INIT(&fields[28]), NULL},
+  {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "string_value"), UPB_TABVALUE_PTR_INIT(&fields[90]), &strentries[182]},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "is_extension"), UPB_TABVALUE_PTR_INIT(&fields[21]), NULL},
-  {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "name_part"), UPB_TABVALUE_PTR_INIT(&fields[42]), NULL},
-  {UPB_TABKEY_STR("\016", "\000", "\000", "\000", "LABEL_REQUIRED"), UPB_TABVALUE_INT_INIT(2), &strentries[162]},
+  {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "is_extension"), UPB_TABVALUE_PTR_INIT(&fields[30]), NULL},
+  {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "name_part"), UPB_TABVALUE_PTR_INIT(&fields[58]), NULL},
+  {UPB_TABKEY_STR("\016", "\000", "\000", "\000", "LABEL_REQUIRED"), UPB_TABVALUE_INT_INIT(2), &strentries[190]},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_STR("\016", "\000", "\000", "\000", "LABEL_REPEATED"), UPB_TABVALUE_INT_INIT(3), NULL},
   {UPB_TABKEY_STR("\016", "\000", "\000", "\000", "LABEL_OPTIONAL"), UPB_TABVALUE_INT_INIT(1), NULL},
@@ -5274,17 +5864,17 @@
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "TYPE_STRING"), UPB_TABVALUE_INT_INIT(9), NULL},
-  {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "TYPE_FLOAT"), UPB_TABVALUE_INT_INIT(2), &strentries[193]},
+  {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "TYPE_FLOAT"), UPB_TABVALUE_INT_INIT(2), &strentries[221]},
   {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "TYPE_DOUBLE"), UPB_TABVALUE_INT_INIT(1), NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "TYPE_INT32"), UPB_TABVALUE_INT_INIT(5), NULL},
   {UPB_TABKEY_STR("\015", "\000", "\000", "\000", "TYPE_SFIXED32"), UPB_TABVALUE_INT_INIT(15), NULL},
   {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "TYPE_FIXED32"), UPB_TABVALUE_INT_INIT(7), NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "TYPE_MESSAGE"), UPB_TABVALUE_INT_INIT(11), &strentries[194]},
+  {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "TYPE_MESSAGE"), UPB_TABVALUE_INT_INIT(11), &strentries[222]},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "TYPE_INT64"), UPB_TABVALUE_INT_INIT(3), &strentries[191]},
+  {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "TYPE_INT64"), UPB_TABVALUE_INT_INIT(3), &strentries[219]},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
@@ -5292,7 +5882,7 @@
   {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "TYPE_ENUM"), UPB_TABVALUE_INT_INIT(14), NULL},
   {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "TYPE_UINT32"), UPB_TABVALUE_INT_INIT(13), NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "TYPE_UINT64"), UPB_TABVALUE_INT_INIT(4), &strentries[190]},
+  {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "TYPE_UINT64"), UPB_TABVALUE_INT_INIT(4), &strentries[218]},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_STR("\015", "\000", "\000", "\000", "TYPE_SFIXED64"), UPB_TABVALUE_INT_INIT(16), NULL},
   {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "TYPE_BYTES"), UPB_TABVALUE_INT_INIT(12), NULL},
@@ -5302,131 +5892,110 @@
   {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "TYPE_SINT32"), UPB_TABVALUE_INT_INIT(17), NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "CORD"), UPB_TABVALUE_INT_INIT(1), NULL},
-  {UPB_TABKEY_STR("\006", "\000", "\000", "\000", "STRING"), UPB_TABVALUE_INT_INIT(0), &strentries[197]},
+  {UPB_TABKEY_STR("\006", "\000", "\000", "\000", "STRING"), UPB_TABVALUE_INT_INIT(0), &strentries[225]},
   {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "STRING_PIECE"), UPB_TABVALUE_INT_INIT(2), NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "JS_NORMAL"), UPB_TABVALUE_INT_INIT(0), NULL},
+  {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "JS_NUMBER"), UPB_TABVALUE_INT_INIT(2), NULL},
+  {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "JS_STRING"), UPB_TABVALUE_INT_INIT(1), NULL},
   {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "CODE_SIZE"), UPB_TABVALUE_INT_INIT(2), NULL},
-  {UPB_TABKEY_STR("\005", "\000", "\000", "\000", "SPEED"), UPB_TABVALUE_INT_INIT(1), &strentries[203]},
+  {UPB_TABKEY_STR("\005", "\000", "\000", "\000", "SPEED"), UPB_TABVALUE_INT_INIT(1), &strentries[235]},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
   {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "LITE_RUNTIME"), UPB_TABVALUE_INT_INIT(3), NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\047", "\000", "\000", "\000", "google.protobuf.SourceCodeInfo.Location"), UPB_TABVALUE_PTR_INIT(&msgs[17]), NULL},
-  {UPB_TABKEY_STR("\043", "\000", "\000", "\000", "google.protobuf.UninterpretedOption"), UPB_TABVALUE_PTR_INIT(&msgs[18]), NULL},
-  {UPB_TABKEY_STR("\043", "\000", "\000", "\000", "google.protobuf.FileDescriptorProto"), UPB_TABVALUE_PTR_INIT(&msgs[8]), NULL},
-  {UPB_TABKEY_STR("\045", "\000", "\000", "\000", "google.protobuf.MethodDescriptorProto"), UPB_TABVALUE_PTR_INIT(&msgs[12]), NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\040", "\000", "\000", "\000", "google.protobuf.EnumValueOptions"), UPB_TABVALUE_PTR_INIT(&msgs[5]), NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\037", "\000", "\000", "\000", "google.protobuf.DescriptorProto"), UPB_TABVALUE_PTR_INIT(&msgs[0]), &strentries[228]},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\036", "\000", "\000", "\000", "google.protobuf.SourceCodeInfo"), UPB_TABVALUE_PTR_INIT(&msgs[16]), NULL},
-  {UPB_TABKEY_STR("\051", "\000", "\000", "\000", "google.protobuf.FieldDescriptorProto.Type"), UPB_TABVALUE_PTR_INIT(&enums[1]), NULL},
-  {UPB_TABKEY_STR("\056", "\000", "\000", "\000", "google.protobuf.DescriptorProto.ExtensionRange"), UPB_TABVALUE_PTR_INIT(&msgs[1]), NULL},
-  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_STR("\050", "\000", "\000", "\000", "google.protobuf.EnumValueDescriptorProto"), UPB_TABVALUE_PTR_INIT(&msgs[4]), NULL},
-  {UPB_TABKEY_STR("\034", "\000", "\000", "\000", "google.protobuf.FieldOptions"), UPB_TABVALUE_PTR_INIT(&msgs[7]), NULL},
-  {UPB_TABKEY_STR("\033", "\000", "\000", "\000", "google.protobuf.FileOptions"), UPB_TABVALUE_PTR_INIT(&msgs[10]), NULL},
-  {UPB_TABKEY_STR("\043", "\000", "\000", "\000", "google.protobuf.EnumDescriptorProto"), UPB_TABVALUE_PTR_INIT(&msgs[2]), &strentries[233]},
-  {UPB_TABKEY_STR("\052", "\000", "\000", "\000", "google.protobuf.FieldDescriptorProto.Label"), UPB_TABVALUE_PTR_INIT(&enums[0]), NULL},
-  {UPB_TABKEY_STR("\046", "\000", "\000", "\000", "google.protobuf.ServiceDescriptorProto"), UPB_TABVALUE_PTR_INIT(&msgs[14]), NULL},
-  {UPB_TABKEY_STR("\042", "\000", "\000", "\000", "google.protobuf.FieldOptions.CType"), UPB_TABVALUE_PTR_INIT(&enums[2]), &strentries[229]},
-  {UPB_TABKEY_STR("\041", "\000", "\000", "\000", "google.protobuf.FileDescriptorSet"), UPB_TABVALUE_PTR_INIT(&msgs[9]), &strentries[235]},
-  {UPB_TABKEY_STR("\033", "\000", "\000", "\000", "google.protobuf.EnumOptions"), UPB_TABVALUE_PTR_INIT(&msgs[3]), NULL},
-  {UPB_TABKEY_STR("\044", "\000", "\000", "\000", "google.protobuf.FieldDescriptorProto"), UPB_TABVALUE_PTR_INIT(&msgs[6]), NULL},
-  {UPB_TABKEY_STR("\050", "\000", "\000", "\000", "google.protobuf.FileOptions.OptimizeMode"), UPB_TABVALUE_PTR_INIT(&enums[3]), &strentries[221]},
-  {UPB_TABKEY_STR("\036", "\000", "\000", "\000", "google.protobuf.ServiceOptions"), UPB_TABVALUE_PTR_INIT(&msgs[15]), NULL},
-  {UPB_TABKEY_STR("\036", "\000", "\000", "\000", "google.protobuf.MessageOptions"), UPB_TABVALUE_PTR_INIT(&msgs[11]), NULL},
-  {UPB_TABKEY_STR("\035", "\000", "\000", "\000", "google.protobuf.MethodOptions"), UPB_TABVALUE_PTR_INIT(&msgs[13]), &strentries[226]},
-  {UPB_TABKEY_STR("\054", "\000", "\000", "\000", "google.protobuf.UninterpretedOption.NamePart"), UPB_TABVALUE_PTR_INIT(&msgs[19]), NULL},
 };
 
-static const upb_tabent intentries[14] = {
+static const upb_tabent intentries[18] = {
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[73]), NULL},
+  {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[98]), NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[71]), NULL},
+  {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[97]), NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[77]), NULL},
+  {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[99]), NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[75]), NULL},
+  {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[95]), NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[76]), NULL},
+  {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[96]), NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[74]), NULL},
+  {UPB_TABKEY_NUM(33), UPB_TABVALUE_PTR_INIT(&fields[11]), NULL},
   {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
-  {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[72]), NULL},
+  {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[100]), NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_NUM(33), UPB_TABVALUE_PTR_INIT(&fields[14]), NULL},
+  {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL},
+  {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[101]), NULL},
 };
 
-static const upb_tabval arrays[232] = {
+static const upb_tabval arrays[184] = {
   UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_PTR_INIT(&fields[38]),
-  UPB_TABVALUE_PTR_INIT(&fields[16]),
-  UPB_TABVALUE_PTR_INIT(&fields[44]),
-  UPB_TABVALUE_PTR_INIT(&fields[9]),
-  UPB_TABVALUE_PTR_INIT(&fields[15]),
-  UPB_TABVALUE_PTR_INIT(&fields[14]),
+  UPB_TABVALUE_PTR_INIT(&fields[52]),
+  UPB_TABVALUE_PTR_INIT(&fields[25]),
+  UPB_TABVALUE_PTR_INIT(&fields[60]),
+  UPB_TABVALUE_PTR_INIT(&fields[19]),
+  UPB_TABVALUE_PTR_INIT(&fields[24]),
+  UPB_TABVALUE_PTR_INIT(&fields[22]),
+  UPB_TABVALUE_PTR_INIT(&fields[68]),
+  UPB_TABVALUE_PTR_INIT(&fields[65]),
+  UPB_TABVALUE_PTR_INIT(&fields[83]),
+  UPB_TABVALUE_PTR_INIT(&fields[82]),
+  UPB_TABVALUE_EMPTY_INIT,
+  UPB_TABVALUE_PTR_INIT(&fields[89]),
+  UPB_TABVALUE_PTR_INIT(&fields[18]),
+  UPB_TABVALUE_EMPTY_INIT,
+  UPB_TABVALUE_PTR_INIT(&fields[88]),
+  UPB_TABVALUE_PTR_INIT(&fields[17]),
+  UPB_TABVALUE_EMPTY_INIT,
   UPB_TABVALUE_PTR_INIT(&fields[49]),
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_PTR_INIT(&fields[66]),
-  UPB_TABVALUE_PTR_INIT(&fields[8]),
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_PTR_INIT(&fields[40]),
-  UPB_TABVALUE_PTR_INIT(&fields[78]),
-  UPB_TABVALUE_PTR_INIT(&fields[50]),
+  UPB_TABVALUE_PTR_INIT(&fields[102]),
+  UPB_TABVALUE_PTR_INIT(&fields[74]),
   UPB_TABVALUE_EMPTY_INIT,
   UPB_TABVALUE_EMPTY_INIT,
   UPB_TABVALUE_PTR_INIT(&fields[1]),
+  UPB_TABVALUE_PTR_INIT(&fields[13]),
   UPB_TABVALUE_EMPTY_INIT,
+  UPB_TABVALUE_PTR_INIT(&fields[53]),
+  UPB_TABVALUE_PTR_INIT(&fields[62]),
+  UPB_TABVALUE_PTR_INIT(&fields[73]),
   UPB_TABVALUE_EMPTY_INIT,
+  UPB_TABVALUE_PTR_INIT(&fields[15]),
   UPB_TABVALUE_EMPTY_INIT,
+  UPB_TABVALUE_PTR_INIT(&fields[55]),
+  UPB_TABVALUE_PTR_INIT(&fields[21]),
+  UPB_TABVALUE_PTR_INIT(&fields[63]),
+  UPB_TABVALUE_PTR_INIT(&fields[40]),
+  UPB_TABVALUE_PTR_INIT(&fields[93]),
+  UPB_TABVALUE_PTR_INIT(&fields[94]),
+  UPB_TABVALUE_PTR_INIT(&fields[7]),
+  UPB_TABVALUE_PTR_INIT(&fields[71]),
+  UPB_TABVALUE_PTR_INIT(&fields[66]),
+  UPB_TABVALUE_PTR_INIT(&fields[38]),
   UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_PTR_INIT(&fields[37]),
-  UPB_TABVALUE_PTR_INIT(&fields[47]),
-  UPB_TABVALUE_PTR_INIT(&fields[52]),
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
+  UPB_TABVALUE_PTR_INIT(&fields[6]),
+  UPB_TABVALUE_PTR_INIT(&fields[77]),
+  UPB_TABVALUE_PTR_INIT(&fields[10]),
   UPB_TABVALUE_EMPTY_INIT,
   UPB_TABVALUE_PTR_INIT(&fields[41]),
-  UPB_TABVALUE_PTR_INIT(&fields[12]),
-  UPB_TABVALUE_PTR_INIT(&fields[46]),
-  UPB_TABVALUE_PTR_INIT(&fields[27]),
+  UPB_TABVALUE_PTR_INIT(&fields[39]),
+  UPB_TABVALUE_EMPTY_INIT,
+  UPB_TABVALUE_EMPTY_INIT,
+  UPB_TABVALUE_EMPTY_INIT,
+  UPB_TABVALUE_PTR_INIT(&fields[103]),
+  UPB_TABVALUE_EMPTY_INIT,
+  UPB_TABVALUE_PTR_INIT(&fields[54]),
+  UPB_TABVALUE_PTR_INIT(&fields[76]),
+  UPB_TABVALUE_PTR_INIT(&fields[8]),
+  UPB_TABVALUE_PTR_INIT(&fields[47]),
+  UPB_TABVALUE_PTR_INIT(&fields[20]),
+  UPB_TABVALUE_PTR_INIT(&fields[85]),
+  UPB_TABVALUE_PTR_INIT(&fields[23]),
   UPB_TABVALUE_PTR_INIT(&fields[69]),
-  UPB_TABVALUE_PTR_INIT(&fields[70]),
-  UPB_TABVALUE_PTR_INIT(&fields[4]),
-  UPB_TABVALUE_PTR_INIT(&fields[51]),
+  UPB_TABVALUE_PTR_INIT(&fields[86]),
+  UPB_TABVALUE_PTR_INIT(&fields[80]),
+  UPB_TABVALUE_PTR_INIT(&fields[104]),
+  UPB_TABVALUE_PTR_INIT(&fields[91]),
   UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_PTR_INIT(&fields[3]),
-  UPB_TABVALUE_PTR_INIT(&fields[58]),
-  UPB_TABVALUE_PTR_INIT(&fields[6]),
+  UPB_TABVALUE_PTR_INIT(&fields[26]),
   UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_PTR_INIT(&fields[28]),
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_PTR_INIT(&fields[11]),
-  UPB_TABVALUE_PTR_INIT(&fields[79]),
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
+  UPB_TABVALUE_PTR_INIT(&fields[35]),
   UPB_TABVALUE_EMPTY_INIT,
   UPB_TABVALUE_EMPTY_INIT,
   UPB_TABVALUE_EMPTY_INIT,
@@ -5434,134 +6003,80 @@
   UPB_TABVALUE_EMPTY_INIT,
   UPB_TABVALUE_EMPTY_INIT,
   UPB_TABVALUE_PTR_INIT(&fields[34]),
-  UPB_TABVALUE_PTR_INIT(&fields[57]),
-  UPB_TABVALUE_PTR_INIT(&fields[5]),
+  UPB_TABVALUE_PTR_INIT(&fields[67]),
+  UPB_TABVALUE_PTR_INIT(&fields[33]),
+  UPB_TABVALUE_PTR_INIT(&fields[27]),
+  UPB_TABVALUE_EMPTY_INIT,
+  UPB_TABVALUE_EMPTY_INIT,
+  UPB_TABVALUE_EMPTY_INIT,
+  UPB_TABVALUE_EMPTY_INIT,
+  UPB_TABVALUE_PTR_INIT(&fields[3]),
   UPB_TABVALUE_PTR_INIT(&fields[32]),
-  UPB_TABVALUE_PTR_INIT(&fields[10]),
-  UPB_TABVALUE_PTR_INIT(&fields[63]),
-  UPB_TABVALUE_PTR_INIT(&fields[13]),
-  UPB_TABVALUE_PTR_INIT(&fields[53]),
-  UPB_TABVALUE_PTR_INIT(&fields[64]),
-  UPB_TABVALUE_PTR_INIT(&fields[61]),
-  UPB_TABVALUE_PTR_INIT(&fields[80]),
+  UPB_TABVALUE_PTR_INIT(&fields[81]),
   UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_PTR_INIT(&fields[17]),
+  UPB_TABVALUE_PTR_INIT(&fields[31]),
   UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_PTR_INIT(&fields[26]),
+  UPB_TABVALUE_EMPTY_INIT,
+  UPB_TABVALUE_PTR_INIT(&fields[12]),
   UPB_TABVALUE_EMPTY_INIT,
   UPB_TABVALUE_EMPTY_INIT,
   UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_PTR_INIT(&fields[25]),
-  UPB_TABVALUE_PTR_INIT(&fields[48]),
-  UPB_TABVALUE_PTR_INIT(&fields[24]),
-  UPB_TABVALUE_PTR_INIT(&fields[18]),
-  UPB_TABVALUE_EMPTY_INIT,
+  UPB_TABVALUE_PTR_INIT(&fields[36]),
   UPB_TABVALUE_EMPTY_INIT,
   UPB_TABVALUE_EMPTY_INIT,
   UPB_TABVALUE_EMPTY_INIT,
   UPB_TABVALUE_PTR_INIT(&fields[2]),
-  UPB_TABVALUE_PTR_INIT(&fields[23]),
-  UPB_TABVALUE_PTR_INIT(&fields[62]),
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_PTR_INIT(&fields[22]),
   UPB_TABVALUE_EMPTY_INIT,
   UPB_TABVALUE_EMPTY_INIT,
   UPB_TABVALUE_EMPTY_INIT,
   UPB_TABVALUE_EMPTY_INIT,
+  UPB_TABVALUE_PTR_INIT(&fields[64]),
+  UPB_TABVALUE_PTR_INIT(&fields[5]),
+  UPB_TABVALUE_PTR_INIT(&fields[37]),
+  UPB_TABVALUE_EMPTY_INIT,
+  UPB_TABVALUE_PTR_INIT(&fields[46]),
+  UPB_TABVALUE_PTR_INIT(&fields[61]),
+  UPB_TABVALUE_PTR_INIT(&fields[9]),
   UPB_TABVALUE_EMPTY_INIT,
   UPB_TABVALUE_EMPTY_INIT,
   UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_PTR_INIT(&fields[31]),
   UPB_TABVALUE_PTR_INIT(&fields[45]),
   UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_PTR_INIT(&fields[39]),
-  UPB_TABVALUE_PTR_INIT(&fields[20]),
   UPB_TABVALUE_PTR_INIT(&fields[56]),
-  UPB_TABVALUE_PTR_INIT(&fields[55]),
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_PTR_INIT(&fields[35]),
-  UPB_TABVALUE_PTR_INIT(&fields[33]),
-  UPB_TABVALUE_PTR_INIT(&fields[54]),
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_PTR_INIT(&fields[30]),
-  UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_PTR_INIT(&fields[59]),
-  UPB_TABVALUE_PTR_INIT(&fields[65]),
   UPB_TABVALUE_PTR_INIT(&fields[29]),
-  UPB_TABVALUE_PTR_INIT(&fields[68]),
+  UPB_TABVALUE_PTR_INIT(&fields[75]),
+  UPB_TABVALUE_PTR_INIT(&fields[70]),
+  UPB_TABVALUE_PTR_INIT(&fields[4]),
+  UPB_TABVALUE_PTR_INIT(&fields[84]),
   UPB_TABVALUE_EMPTY_INIT,
   UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_PTR_INIT(&fields[36]),
-  UPB_TABVALUE_PTR_INIT(&fields[19]),
-  UPB_TABVALUE_PTR_INIT(&fields[60]),
+  UPB_TABVALUE_PTR_INIT(&fields[50]),
+  UPB_TABVALUE_EMPTY_INIT,
+  UPB_TABVALUE_PTR_INIT(&fields[57]),
+  UPB_TABVALUE_PTR_INIT(&fields[48]),
+  UPB_TABVALUE_PTR_INIT(&fields[72]),
+  UPB_TABVALUE_EMPTY_INIT,
+  UPB_TABVALUE_EMPTY_INIT,
+  UPB_TABVALUE_PTR_INIT(&fields[44]),
+  UPB_TABVALUE_EMPTY_INIT,
+  UPB_TABVALUE_PTR_INIT(&fields[78]),
+  UPB_TABVALUE_PTR_INIT(&fields[87]),
+  UPB_TABVALUE_PTR_INIT(&fields[42]),
+  UPB_TABVALUE_PTR_INIT(&fields[92]),
+  UPB_TABVALUE_EMPTY_INIT,
   UPB_TABVALUE_PTR_INIT(&fields[43]),
-  UPB_TABVALUE_PTR_INIT(&fields[7]),
-  UPB_TABVALUE_PTR_INIT(&fields[67]),
+  UPB_TABVALUE_EMPTY_INIT,
+  UPB_TABVALUE_EMPTY_INIT,
+  UPB_TABVALUE_PTR_INIT(&fields[51]),
+  UPB_TABVALUE_PTR_INIT(&fields[28]),
+  UPB_TABVALUE_PTR_INIT(&fields[79]),
+  UPB_TABVALUE_PTR_INIT(&fields[59]),
+  UPB_TABVALUE_PTR_INIT(&fields[16]),
+  UPB_TABVALUE_PTR_INIT(&fields[90]),
   UPB_TABVALUE_PTR_INIT(&fields[0]),
   UPB_TABVALUE_EMPTY_INIT,
-  UPB_TABVALUE_PTR_INIT(&fields[42]),
-  UPB_TABVALUE_PTR_INIT(&fields[21]),
+  UPB_TABVALUE_PTR_INIT(&fields[58]),
+  UPB_TABVALUE_PTR_INIT(&fields[30]),
   UPB_TABVALUE_EMPTY_INIT,
   UPB_TABVALUE_PTR_INIT("LABEL_OPTIONAL"),
   UPB_TABVALUE_PTR_INIT("LABEL_REQUIRED"),
@@ -5588,21 +6103,69 @@
   UPB_TABVALUE_PTR_INIT("STRING"),
   UPB_TABVALUE_PTR_INIT("CORD"),
   UPB_TABVALUE_PTR_INIT("STRING_PIECE"),
+  UPB_TABVALUE_PTR_INIT("JS_NORMAL"),
+  UPB_TABVALUE_PTR_INIT("JS_STRING"),
+  UPB_TABVALUE_PTR_INIT("JS_NUMBER"),
   UPB_TABVALUE_EMPTY_INIT,
   UPB_TABVALUE_PTR_INIT("SPEED"),
   UPB_TABVALUE_PTR_INIT("CODE_SIZE"),
   UPB_TABVALUE_PTR_INIT("LITE_RUNTIME"),
 };
 
-static const upb_symtab symtab = UPB_SYMTAB_INIT(UPB_STRTABLE_INIT(24, 31, UPB_CTYPE_PTR, 5, &strentries[204]), &reftables[210], &reftables[211]);
-
-const upb_symtab *upbdefs_google_protobuf_descriptor(const void *owner) {
-  upb_symtab_ref(&symtab, owner);
-  return &symtab;
-}
-
 #ifdef UPB_DEBUG_REFS
-static upb_inttable reftables[212] = {
+static upb_inttable reftables[264] = {
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
+  UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
   UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
   UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
   UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR),
@@ -5818,6 +6381,45 @@
 };
 #endif
 
+static const upb_msgdef *refm(const upb_msgdef *m, const void *owner) {
+  upb_msgdef_ref(m, owner);
+  return m;
+}
+
+static const upb_enumdef *refe(const upb_enumdef *e, const void *owner) {
+  upb_enumdef_ref(e, owner);
+  return e;
+}
+
+/* Public API. */
+const upb_msgdef *upbdefs_google_protobuf_DescriptorProto_get(const void *owner) { return refm(&msgs[0], owner); }
+const upb_msgdef *upbdefs_google_protobuf_DescriptorProto_ExtensionRange_get(const void *owner) { return refm(&msgs[1], owner); }
+const upb_msgdef *upbdefs_google_protobuf_DescriptorProto_ReservedRange_get(const void *owner) { return refm(&msgs[2], owner); }
+const upb_msgdef *upbdefs_google_protobuf_EnumDescriptorProto_get(const void *owner) { return refm(&msgs[3], owner); }
+const upb_msgdef *upbdefs_google_protobuf_EnumOptions_get(const void *owner) { return refm(&msgs[4], owner); }
+const upb_msgdef *upbdefs_google_protobuf_EnumValueDescriptorProto_get(const void *owner) { return refm(&msgs[5], owner); }
+const upb_msgdef *upbdefs_google_protobuf_EnumValueOptions_get(const void *owner) { return refm(&msgs[6], owner); }
+const upb_msgdef *upbdefs_google_protobuf_FieldDescriptorProto_get(const void *owner) { return refm(&msgs[7], owner); }
+const upb_msgdef *upbdefs_google_protobuf_FieldOptions_get(const void *owner) { return refm(&msgs[8], owner); }
+const upb_msgdef *upbdefs_google_protobuf_FileDescriptorProto_get(const void *owner) { return refm(&msgs[9], owner); }
+const upb_msgdef *upbdefs_google_protobuf_FileDescriptorSet_get(const void *owner) { return refm(&msgs[10], owner); }
+const upb_msgdef *upbdefs_google_protobuf_FileOptions_get(const void *owner) { return refm(&msgs[11], owner); }
+const upb_msgdef *upbdefs_google_protobuf_MessageOptions_get(const void *owner) { return refm(&msgs[12], owner); }
+const upb_msgdef *upbdefs_google_protobuf_MethodDescriptorProto_get(const void *owner) { return refm(&msgs[13], owner); }
+const upb_msgdef *upbdefs_google_protobuf_MethodOptions_get(const void *owner) { return refm(&msgs[14], owner); }
+const upb_msgdef *upbdefs_google_protobuf_OneofDescriptorProto_get(const void *owner) { return refm(&msgs[15], owner); }
+const upb_msgdef *upbdefs_google_protobuf_ServiceDescriptorProto_get(const void *owner) { return refm(&msgs[16], owner); }
+const upb_msgdef *upbdefs_google_protobuf_ServiceOptions_get(const void *owner) { return refm(&msgs[17], owner); }
+const upb_msgdef *upbdefs_google_protobuf_SourceCodeInfo_get(const void *owner) { return refm(&msgs[18], owner); }
+const upb_msgdef *upbdefs_google_protobuf_SourceCodeInfo_Location_get(const void *owner) { return refm(&msgs[19], owner); }
+const upb_msgdef *upbdefs_google_protobuf_UninterpretedOption_get(const void *owner) { return refm(&msgs[20], owner); }
+const upb_msgdef *upbdefs_google_protobuf_UninterpretedOption_NamePart_get(const void *owner) { return refm(&msgs[21], owner); }
+
+const upb_enumdef *upbdefs_google_protobuf_FieldDescriptorProto_Label_get(const void *owner) { return refe(&enums[0], owner); }
+const upb_enumdef *upbdefs_google_protobuf_FieldDescriptorProto_Type_get(const void *owner) { return refe(&enums[1], owner); }
+const upb_enumdef *upbdefs_google_protobuf_FieldOptions_CType_get(const void *owner) { return refe(&enums[2], owner); }
+const upb_enumdef *upbdefs_google_protobuf_FieldOptions_JSType_get(const void *owner) { return refe(&enums[3], owner); }
+const upb_enumdef *upbdefs_google_protobuf_FileOptions_OptimizeMode_get(const void *owner) { return refe(&enums[4], owner); }
 /*
 ** XXX: The routines in this file that consume a string do not currently
 ** support having the string span buffers.  In the future, as upb_sink and
@@ -5831,14 +6433,10 @@
 #include <stdlib.h>
 #include <string.h>
 
-/* upb_deflist is an internal-only dynamic array for storing a growing list of
- * upb_defs. */
-typedef struct {
-  upb_def **defs;
-  size_t len;
-  size_t size;
-  bool owned;
-} upb_deflist;
+/* Compares a NULL-terminated string with a non-NULL-terminated string. */
+static bool upb_streq(const char *str, const char *buf, size_t n) {
+  return strlen(str) == n && memcmp(str, buf, n) == 0;
+}
 
 /* We keep a stack of all the messages scopes we are currently in, as well as
  * the top-level file scope.  This is necessary to correctly qualify the
@@ -5849,6 +6447,8 @@
   /* Index of the first def that is under this scope.  For msgdefs, the
    * msgdef itself is at start-1. */
   int start;
+  uint32_t oneof_start;
+  uint32_t oneof_index;
 } upb_descreader_frame;
 
 /* The maximum number of nested declarations that are allowed, ie.
@@ -5865,9 +6465,11 @@
 
 struct upb_descreader {
   upb_sink sink;
-  upb_deflist defs;
+  upb_inttable files;
+  upb_filedef *file;  /* The last file in files. */
   upb_descreader_frame stack[UPB_MAX_MESSAGE_NESTING];
   int stack_len;
+  upb_inttable oneofs;
 
   uint32_t number;
   char *name;
@@ -5880,7 +6482,7 @@
 };
 
 static char *upb_strndup(const char *buf, size_t n) {
-  char *ret = malloc(n + 1);
+  char *ret = upb_gmalloc(n + 1);
   if (!ret) return NULL;
   memcpy(ret, buf, n);
   ret[n] = '\0';
@@ -5894,9 +6496,12 @@
  * Caller owns a ref on the returned string. */
 static char *upb_join(const char *base, const char *name) {
   if (!base || strlen(base) == 0) {
-    return upb_strdup(name);
+    return upb_gstrdup(name);
   } else {
-    char *ret = malloc(strlen(base) + strlen(name) + 2);
+    char *ret = upb_gmalloc(strlen(base) + strlen(name) + 2);
+    if (!ret) {
+      return NULL;
+    }
     ret[0] = '\0';
     strcat(ret, base);
     strcat(ret, ".");
@@ -5905,57 +6510,21 @@
   }
 }
 
-
-/* upb_deflist ****************************************************************/
-
-void upb_deflist_init(upb_deflist *l) {
-  l->size = 0;
-  l->defs = NULL;
-  l->len = 0;
-  l->owned = true;
-}
-
-void upb_deflist_uninit(upb_deflist *l) {
-  size_t i;
-  if (l->owned)
-    for(i = 0; i < l->len; i++)
-      upb_def_unref(l->defs[i], l);
-  free(l->defs);
-}
-
-bool upb_deflist_push(upb_deflist *l, upb_def *d) {
-  if(++l->len >= l->size) {
-    size_t new_size = UPB_MAX(l->size, 4);
-    new_size *= 2;
-    l->defs = realloc(l->defs, new_size * sizeof(void *));
-    if (!l->defs) return false;
-    l->size = new_size;
-  }
-  l->defs[l->len - 1] = d;
-  return true;
-}
-
-void upb_deflist_donaterefs(upb_deflist *l, void *owner) {
-  size_t i;
-  assert(l->owned);
-  for (i = 0; i < l->len; i++)
-    upb_def_donateref(l->defs[i], l, owner);
-  l->owned = false;
-}
-
-static upb_def *upb_deflist_last(upb_deflist *l) {
-  return l->defs[l->len-1];
-}
-
 /* Qualify the defname for all defs starting with offset "start" with "str". */
-static void upb_deflist_qualify(upb_deflist *l, char *str, int32_t start) {
-  uint32_t i;
-  for (i = start; i < l->len; i++) {
-    upb_def *def = l->defs[i];
+static bool upb_descreader_qualify(upb_filedef *f, char *str, int32_t start) {
+  size_t i;
+  for (i = start; i < upb_filedef_defcount(f); i++) {
+    upb_def *def = upb_filedef_mutabledef(f, i);
     char *name = upb_join(str, upb_def_fullname(def));
+    if (!name) {
+      /* Need better logic here; at this point we've qualified some names but
+       * not others. */
+      return false;
+    }
     upb_def_setfullname(def, name, NULL);
-    free(name);
+    upb_gfree(name);
   }
+  return true;
 }
 
 
@@ -5963,63 +6532,178 @@
 
 static upb_msgdef *upb_descreader_top(upb_descreader *r) {
   int index;
-  assert(r->stack_len > 1);
+  UPB_ASSERT(r->stack_len > 1);
   index = r->stack[r->stack_len-1].start - 1;
-  assert(index >= 0);
-  return upb_downcast_msgdef_mutable(r->defs.defs[index]);
+  UPB_ASSERT(index >= 0);
+  return upb_downcast_msgdef_mutable(upb_filedef_mutabledef(r->file, index));
 }
 
 static upb_def *upb_descreader_last(upb_descreader *r) {
-  return upb_deflist_last(&r->defs);
+  return upb_filedef_mutabledef(r->file, upb_filedef_defcount(r->file) - 1);
 }
 
 /* Start/end handlers for FileDescriptorProto and DescriptorProto (the two
  * entities that have names and can contain sub-definitions. */
 void upb_descreader_startcontainer(upb_descreader *r) {
   upb_descreader_frame *f = &r->stack[r->stack_len++];
-  f->start = r->defs.len;
+  f->start = upb_filedef_defcount(r->file);
+  f->oneof_start = upb_inttable_count(&r->oneofs);
+  f->oneof_index = 0;
   f->name = NULL;
 }
 
-void upb_descreader_endcontainer(upb_descreader *r) {
-  upb_descreader_frame *f = &r->stack[--r->stack_len];
-  upb_deflist_qualify(&r->defs, f->name, f->start);
-  free(f->name);
+bool upb_descreader_endcontainer(upb_descreader *r) {
+  upb_descreader_frame *f = &r->stack[r->stack_len - 1];
+
+  while (upb_inttable_count(&r->oneofs) > f->oneof_start) {
+    upb_oneofdef *o = upb_value_getptr(upb_inttable_pop(&r->oneofs));
+    bool ok = upb_msgdef_addoneof(upb_descreader_top(r), o, &r->oneofs, NULL);
+    UPB_ASSERT(ok);
+  }
+
+  if (!upb_descreader_qualify(r->file, f->name, f->start)) {
+    return false;
+  }
+  upb_gfree(f->name);
   f->name = NULL;
+
+  r->stack_len--;
+  return true;
 }
 
 void upb_descreader_setscopename(upb_descreader *r, char *str) {
   upb_descreader_frame *f = &r->stack[r->stack_len-1];
-  free(f->name);
+  upb_gfree(f->name);
   f->name = str;
 }
 
-/* Handlers for google.protobuf.FileDescriptorProto. */
-static bool file_startmsg(void *r, const void *hd) {
+static upb_oneofdef *upb_descreader_getoneof(upb_descreader *r,
+                                             uint32_t index) {
+  bool found;
+  upb_value val;
+  upb_descreader_frame *f = &r->stack[r->stack_len-1];
+
+  /* DescriptorProto messages can be nested, so we will see the nested messages
+   * between when we see the FieldDescriptorProto and the OneofDescriptorProto.
+   * We need to preserve the oneofs in between these two things. */
+  index += f->oneof_start;
+
+  while (upb_inttable_count(&r->oneofs) <= index) {
+    upb_inttable_push(&r->oneofs, upb_value_ptr(upb_oneofdef_new(&r->oneofs)));
+  }
+
+  found = upb_inttable_lookup(&r->oneofs, index, &val);
+  UPB_ASSERT(found);
+  return upb_value_getptr(val);
+}
+
+/** Handlers for google.protobuf.FileDescriptorSet. ***************************/
+
+static void *fileset_startfile(void *closure, const void *hd) {
+  upb_descreader *r = closure;
+  UPB_UNUSED(hd);
+  r->file = upb_filedef_new(&r->files);
+  upb_inttable_push(&r->files, upb_value_ptr(r->file));
+  return r;
+}
+
+/** Handlers for google.protobuf.FileDescriptorProto. *************************/
+
+static bool file_start(void *closure, const void *hd) {
+  upb_descreader *r = closure;
   UPB_UNUSED(hd);
   upb_descreader_startcontainer(r);
   return true;
 }
 
-static bool file_endmsg(void *closure, const void *hd, upb_status *status) {
+static bool file_end(void *closure, const void *hd, upb_status *status) {
   upb_descreader *r = closure;
   UPB_UNUSED(hd);
   UPB_UNUSED(status);
-  upb_descreader_endcontainer(r);
-  return true;
+  return upb_descreader_endcontainer(r);
+}
+
+static size_t file_onname(void *closure, const void *hd, const char *buf,
+                          size_t n, const upb_bufhandle *handle) {
+  upb_descreader *r = closure;
+  char *name;
+  bool ok;
+  UPB_UNUSED(hd);
+  UPB_UNUSED(handle);
+
+  name = upb_strndup(buf, n);
+  /* XXX: see comment at the top of the file. */
+  ok = upb_filedef_setname(r->file, name, NULL);
+  upb_gfree(name);
+  UPB_ASSERT(ok);
+  return n;
 }
 
 static size_t file_onpackage(void *closure, const void *hd, const char *buf,
                              size_t n, const upb_bufhandle *handle) {
   upb_descreader *r = closure;
+  char *package;
+  bool ok;
   UPB_UNUSED(hd);
   UPB_UNUSED(handle);
+
+  package = upb_strndup(buf, n);
   /* XXX: see comment at the top of the file. */
-  upb_descreader_setscopename(r, upb_strndup(buf, n));
+  upb_descreader_setscopename(r, package);
+  ok = upb_filedef_setpackage(r->file, package, NULL);
+  UPB_ASSERT(ok);
   return n;
 }
 
-/* Handlers for google.protobuf.EnumValueDescriptorProto. */
+static size_t file_onsyntax(void *closure, const void *hd, const char *buf,
+                            size_t n, const upb_bufhandle *handle) {
+  upb_descreader *r = closure;
+  bool ok;
+  UPB_UNUSED(hd);
+  UPB_UNUSED(handle);
+  /* XXX: see comment at the top of the file. */
+  if (upb_streq("proto2", buf, n)) {
+    ok = upb_filedef_setsyntax(r->file, UPB_SYNTAX_PROTO2, NULL);
+  } else if (upb_streq("proto3", buf, n)) {
+    ok = upb_filedef_setsyntax(r->file, UPB_SYNTAX_PROTO3, NULL);
+  } else {
+    ok = false;
+  }
+
+  UPB_ASSERT(ok);
+  return n;
+}
+
+static void *file_startmsg(void *closure, const void *hd) {
+  upb_descreader *r = closure;
+  upb_msgdef *m = upb_msgdef_new(&m);
+  bool ok = upb_filedef_addmsg(r->file, m, &m, NULL);
+  UPB_UNUSED(hd);
+  UPB_ASSERT(ok);
+  return r;
+}
+
+static void *file_startenum(void *closure, const void *hd) {
+  upb_descreader *r = closure;
+  upb_enumdef *e = upb_enumdef_new(&e);
+  bool ok = upb_filedef_addenum(r->file, e, &e, NULL);
+  UPB_UNUSED(hd);
+  UPB_ASSERT(ok);
+  return r;
+}
+
+static void *file_startext(void *closure, const void *hd) {
+  upb_descreader *r = closure;
+  bool ok;
+  r->f = upb_fielddef_new(r);
+  ok = upb_filedef_addext(r->file, r->f, r, NULL);
+  UPB_UNUSED(hd);
+  UPB_ASSERT(ok);
+  return r;
+}
+
+/** Handlers for google.protobuf.EnumValueDescriptorProto. *********************/
+
 static bool enumval_startmsg(void *closure, const void *hd) {
   upb_descreader *r = closure;
   UPB_UNUSED(hd);
@@ -6034,7 +6718,7 @@
   UPB_UNUSED(hd);
   UPB_UNUSED(handle);
   /* XXX: see comment at the top of the file. */
-  free(r->name);
+  upb_gfree(r->name);
   r->name = upb_strndup(buf, n);
   r->saw_name = true;
   return n;
@@ -6059,20 +6743,12 @@
   }
   e = upb_downcast_enumdef_mutable(upb_descreader_last(r));
   upb_enumdef_addval(e, r->name, r->number, status);
-  free(r->name);
+  upb_gfree(r->name);
   r->name = NULL;
   return true;
 }
 
-
-/* Handlers for google.protobuf.EnumDescriptorProto. */
-static bool enum_startmsg(void *closure, const void *hd) {
-  upb_descreader *r = closure;
-  UPB_UNUSED(hd);
-  upb_deflist_push(&r->defs,
-                   upb_enumdef_upcast_mutable(upb_enumdef_new(&r->defs)));
-  return true;
-}
+/** Handlers for google.protobuf.EnumDescriptorProto. *************************/
 
 static bool enum_endmsg(void *closure, const void *hd, upb_status *status) {
   upb_descreader *r = closure;
@@ -6099,16 +6775,17 @@
   UPB_UNUSED(handle);
   /* XXX: see comment at the top of the file. */
   upb_def_setfullname(upb_descreader_last(r), fullname, NULL);
-  free(fullname);
+  upb_gfree(fullname);
   return n;
 }
 
-/* Handlers for google.protobuf.FieldDescriptorProto */
+/** Handlers for google.protobuf.FieldDescriptorProto *************************/
+
 static bool field_startmsg(void *closure, const void *hd) {
   upb_descreader *r = closure;
   UPB_UNUSED(hd);
-  r->f = upb_fielddef_new(&r->defs);
-  free(r->default_string);
+  UPB_ASSERT(r->f);
+  upb_gfree(r->default_string);
   r->default_string = NULL;
 
   /* fielddefs default to packed, but descriptors default to non-packed. */
@@ -6193,9 +6870,9 @@
   UPB_UNUSED(hd);
 
   /* TODO: verify that all required fields were present. */
-  assert(upb_fielddef_number(f) != 0);
-  assert(upb_fielddef_name(f) != NULL);
-  assert((upb_fielddef_subdefname(f) != NULL) == upb_fielddef_hassubdef(f));
+  UPB_ASSERT(upb_fielddef_number(f) != 0);
+  UPB_ASSERT(upb_fielddef_name(f) != NULL);
+  UPB_ASSERT((upb_fielddef_subdefname(f) != NULL) == upb_fielddef_hassubdef(f));
 
   if (r->default_string) {
     if (upb_fielddef_issubmsg(f)) {
@@ -6250,10 +6927,11 @@
 
 static bool field_onnumber(void *closure, const void *hd, int32_t val) {
   upb_descreader *r = closure;
-  bool ok = upb_fielddef_setnumber(r->f, val, NULL);
+  bool ok;
   UPB_UNUSED(hd);
 
-  UPB_ASSERT_VAR(ok, ok);
+  ok = upb_fielddef_setnumber(r->f, val, NULL);
+  UPB_ASSERT(ok);
   return true;
 }
 
@@ -6266,7 +6944,7 @@
 
   /* XXX: see comment at the top of the file. */
   upb_fielddef_setname(r->f, name, NULL);
-  free(name);
+  upb_gfree(name);
   return n;
 }
 
@@ -6279,7 +6957,7 @@
 
   /* XXX: see comment at the top of the file. */
   upb_fielddef_setsubdefname(r->f, name, NULL);
-  free(name);
+  upb_gfree(name);
   return n;
 }
 
@@ -6292,7 +6970,7 @@
 
   /* XXX: see comment at the top of the file. */
   upb_fielddef_setcontainingtypename(r->f, name, NULL);
-  free(name);
+  upb_gfree(name);
   return n;
 }
 
@@ -6305,23 +6983,49 @@
   /* Have to convert from string to the correct type, but we might not know the
    * type yet, so we save it as a string until the end of the field.
    * XXX: see comment at the top of the file. */
-  free(r->default_string);
+  upb_gfree(r->default_string);
   r->default_string = upb_strndup(buf, n);
   return n;
 }
 
-/* Handlers for google.protobuf.DescriptorProto (representing a message). */
-static bool msg_startmsg(void *closure, const void *hd) {
+static bool field_ononeofindex(void *closure, const void *hd, int32_t index) {
+  upb_descreader *r = closure;
+  upb_oneofdef *o = upb_descreader_getoneof(r, index);
+  bool ok = upb_oneofdef_addfield(o, r->f, &r->f, NULL);
+  UPB_UNUSED(hd);
+
+  UPB_ASSERT(ok);
+  return true;
+}
+
+/** Handlers for google.protobuf.OneofDescriptorProto. ************************/
+
+static size_t oneof_name(void *closure, const void *hd, const char *buf,
+                         size_t n, const upb_bufhandle *handle) {
+  upb_descreader *r = closure;
+  upb_descreader_frame *f = &r->stack[r->stack_len-1];
+  upb_oneofdef *o = upb_descreader_getoneof(r, f->oneof_index++);
+  char *name_null_terminated = upb_strndup(buf, n);
+  bool ok = upb_oneofdef_setname(o, name_null_terminated, NULL);
+  UPB_UNUSED(hd);
+  UPB_UNUSED(handle);
+
+  UPB_ASSERT(ok);
+  free(name_null_terminated);
+  return n;
+}
+
+/** Handlers for google.protobuf.DescriptorProto ******************************/
+
+static bool msg_start(void *closure, const void *hd) {
   upb_descreader *r = closure;
   UPB_UNUSED(hd);
 
-  upb_deflist_push(&r->defs,
-                   upb_msgdef_upcast_mutable(upb_msgdef_new(&r->defs)));
   upb_descreader_startcontainer(r);
   return true;
 }
 
-static bool msg_endmsg(void *closure, const void *hd, upb_status *status) {
+static bool msg_end(void *closure, const void *hd, upb_status *status) {
   upb_descreader *r = closure;
   upb_msgdef *m = upb_descreader_top(r);
   UPB_UNUSED(hd);
@@ -6330,12 +7034,11 @@
     upb_status_seterrmsg(status, "Encountered message with no name.");
     return false;
   }
-  upb_descreader_endcontainer(r);
-  return true;
+  return upb_descreader_endcontainer(r);
 }
 
-static size_t msg_onname(void *closure, const void *hd, const char *buf,
-                         size_t n, const upb_bufhandle *handle) {
+static size_t msg_name(void *closure, const void *hd, const char *buf,
+                       size_t n, const upb_bufhandle *handle) {
   upb_descreader *r = closure;
   upb_msgdef *m = upb_descreader_top(r);
   /* XXX: see comment at the top of the file. */
@@ -6348,91 +7051,158 @@
   return n;
 }
 
-static bool msg_onendfield(void *closure, const void *hd) {
+static void *msg_startmsg(void *closure, const void *hd) {
+  upb_descreader *r = closure;
+  upb_msgdef *m = upb_msgdef_new(&m);
+  bool ok = upb_filedef_addmsg(r->file, m, &m, NULL);
+  UPB_UNUSED(hd);
+  UPB_ASSERT(ok);
+  return r;
+}
+
+static void *msg_startext(void *closure, const void *hd) {
+  upb_descreader *r = closure;
+  upb_fielddef *f = upb_fielddef_new(&f);
+  bool ok = upb_filedef_addext(r->file, f, &f, NULL);
+  UPB_UNUSED(hd);
+  UPB_ASSERT(ok);
+  return r;
+}
+
+static void *msg_startfield(void *closure, const void *hd) {
+  upb_descreader *r = closure;
+  r->f = upb_fielddef_new(&r->f);
+  /* We can't add the new field to the message until its name/number are
+   * filled in. */
+  UPB_UNUSED(hd);
+  return r;
+}
+
+static bool msg_endfield(void *closure, const void *hd) {
+  upb_descreader *r = closure;
+  upb_msgdef *m = upb_descreader_top(r);
+  bool ok;
+  UPB_UNUSED(hd);
+
+  /* Oneof fields are added to the msgdef through their oneof, so don't need to
+   * be added here. */
+  if (upb_fielddef_containingoneof(r->f) == NULL) {
+    ok = upb_msgdef_addfield(m, r->f, &r->f, NULL);
+    UPB_ASSERT(ok);
+  }
+  r->f = NULL;
+  return true;
+}
+
+static bool msg_onmapentry(void *closure, const void *hd, bool mapentry) {
   upb_descreader *r = closure;
   upb_msgdef *m = upb_descreader_top(r);
   UPB_UNUSED(hd);
 
-  upb_msgdef_addfield(m, r->f, &r->defs, NULL);
+  upb_msgdef_setmapentry(m, mapentry);
   r->f = NULL;
   return true;
 }
 
-static bool pushextension(void *closure, const void *hd) {
-  upb_descreader *r = closure;
-  UPB_UNUSED(hd);
 
-  assert(upb_fielddef_containingtypename(r->f));
-  upb_fielddef_setisextension(r->f, true);
-  upb_deflist_push(&r->defs, upb_fielddef_upcast_mutable(r->f));
-  r->f = NULL;
-  return true;
-}
 
-#define D(name) upbdefs_google_protobuf_ ## name(s)
+/** Code to register handlers *************************************************/
+
+#define F(msg, field) upbdefs_google_protobuf_ ## msg ## _f_ ## field(m)
 
 static void reghandlers(const void *closure, upb_handlers *h) {
-  const upb_symtab *s = closure;
   const upb_msgdef *m = upb_handlers_msgdef(h);
+  UPB_UNUSED(closure);
 
-  if (m == D(DescriptorProto)) {
-    upb_handlers_setstartmsg(h, &msg_startmsg, NULL);
-    upb_handlers_setendmsg(h, &msg_endmsg, NULL);
-    upb_handlers_setstring(h, D(DescriptorProto_name), &msg_onname, NULL);
-    upb_handlers_setendsubmsg(h, D(DescriptorProto_field), &msg_onendfield,
-                              NULL);
-    upb_handlers_setendsubmsg(h, D(DescriptorProto_extension), &pushextension,
-                              NULL);
-  } else if (m == D(FileDescriptorProto)) {
-    upb_handlers_setstartmsg(h, &file_startmsg, NULL);
-    upb_handlers_setendmsg(h, &file_endmsg, NULL);
-    upb_handlers_setstring(h, D(FileDescriptorProto_package), &file_onpackage,
+  if (upbdefs_google_protobuf_FileDescriptorSet_is(m)) {
+    upb_handlers_setstartsubmsg(h, F(FileDescriptorSet, file),
+                                &fileset_startfile, NULL);
+  } else if (upbdefs_google_protobuf_DescriptorProto_is(m)) {
+    upb_handlers_setstartmsg(h, &msg_start, NULL);
+    upb_handlers_setendmsg(h, &msg_end, NULL);
+    upb_handlers_setstring(h, F(DescriptorProto, name), &msg_name, NULL);
+    upb_handlers_setstartsubmsg(h, F(DescriptorProto, extension), &msg_startext,
+                                NULL);
+    upb_handlers_setstartsubmsg(h, F(DescriptorProto, nested_type),
+                                &msg_startmsg, NULL);
+    upb_handlers_setstartsubmsg(h, F(DescriptorProto, field),
+                                &msg_startfield, NULL);
+    upb_handlers_setendsubmsg(h, F(DescriptorProto, field),
+                              &msg_endfield, NULL);
+    upb_handlers_setstartsubmsg(h, F(DescriptorProto, enum_type),
+                                &file_startenum, NULL);
+  } else if (upbdefs_google_protobuf_FileDescriptorProto_is(m)) {
+    upb_handlers_setstartmsg(h, &file_start, NULL);
+    upb_handlers_setendmsg(h, &file_end, NULL);
+    upb_handlers_setstring(h, F(FileDescriptorProto, name), &file_onname,
                            NULL);
-    upb_handlers_setendsubmsg(h, D(FileDescriptorProto_extension), &pushextension,
-                              NULL);
-  } else if (m == D(EnumValueDescriptorProto)) {
+    upb_handlers_setstring(h, F(FileDescriptorProto, package), &file_onpackage,
+                           NULL);
+    upb_handlers_setstring(h, F(FileDescriptorProto, syntax), &file_onsyntax,
+                           NULL);
+    upb_handlers_setstartsubmsg(h, F(FileDescriptorProto, message_type),
+                                &file_startmsg, NULL);
+    upb_handlers_setstartsubmsg(h, F(FileDescriptorProto, enum_type),
+                                &file_startenum, NULL);
+    upb_handlers_setstartsubmsg(h, F(FileDescriptorProto, extension),
+                                &file_startext, NULL);
+  } else if (upbdefs_google_protobuf_EnumValueDescriptorProto_is(m)) {
     upb_handlers_setstartmsg(h, &enumval_startmsg, NULL);
     upb_handlers_setendmsg(h, &enumval_endmsg, NULL);
-    upb_handlers_setstring(h, D(EnumValueDescriptorProto_name), &enumval_onname, NULL);
-    upb_handlers_setint32(h, D(EnumValueDescriptorProto_number), &enumval_onnumber,
+    upb_handlers_setstring(h, F(EnumValueDescriptorProto, name), &enumval_onname, NULL);
+    upb_handlers_setint32(h, F(EnumValueDescriptorProto, number), &enumval_onnumber,
                           NULL);
-  } else if (m == D(EnumDescriptorProto)) {
-    upb_handlers_setstartmsg(h, &enum_startmsg, NULL);
+  } else if (upbdefs_google_protobuf_EnumDescriptorProto_is(m)) {
     upb_handlers_setendmsg(h, &enum_endmsg, NULL);
-    upb_handlers_setstring(h, D(EnumDescriptorProto_name), &enum_onname, NULL);
-  } else if (m == D(FieldDescriptorProto)) {
+    upb_handlers_setstring(h, F(EnumDescriptorProto, name), &enum_onname, NULL);
+  } else if (upbdefs_google_protobuf_FieldDescriptorProto_is(m)) {
     upb_handlers_setstartmsg(h, &field_startmsg, NULL);
     upb_handlers_setendmsg(h, &field_endmsg, NULL);
-    upb_handlers_setint32(h, D(FieldDescriptorProto_type), &field_ontype,
+    upb_handlers_setint32(h, F(FieldDescriptorProto, type), &field_ontype,
                           NULL);
-    upb_handlers_setint32(h, D(FieldDescriptorProto_label), &field_onlabel,
+    upb_handlers_setint32(h, F(FieldDescriptorProto, label), &field_onlabel,
                           NULL);
-    upb_handlers_setint32(h, D(FieldDescriptorProto_number), &field_onnumber,
+    upb_handlers_setint32(h, F(FieldDescriptorProto, number), &field_onnumber,
                           NULL);
-    upb_handlers_setstring(h, D(FieldDescriptorProto_name), &field_onname,
+    upb_handlers_setstring(h, F(FieldDescriptorProto, name), &field_onname,
                            NULL);
-    upb_handlers_setstring(h, D(FieldDescriptorProto_type_name),
+    upb_handlers_setstring(h, F(FieldDescriptorProto, type_name),
                            &field_ontypename, NULL);
-    upb_handlers_setstring(h, D(FieldDescriptorProto_extendee),
+    upb_handlers_setstring(h, F(FieldDescriptorProto, extendee),
                            &field_onextendee, NULL);
-    upb_handlers_setstring(h, D(FieldDescriptorProto_default_value),
+    upb_handlers_setstring(h, F(FieldDescriptorProto, default_value),
                            &field_ondefaultval, NULL);
-  } else if (m == D(FieldOptions)) {
-    upb_handlers_setbool(h, D(FieldOptions_lazy), &field_onlazy, NULL);
-    upb_handlers_setbool(h, D(FieldOptions_packed), &field_onpacked, NULL);
+    upb_handlers_setint32(h, F(FieldDescriptorProto, oneof_index),
+                          &field_ononeofindex, NULL);
+  } else if (upbdefs_google_protobuf_OneofDescriptorProto_is(m)) {
+    upb_handlers_setstring(h, F(OneofDescriptorProto, name), &oneof_name, NULL);
+  } else if (upbdefs_google_protobuf_FieldOptions_is(m)) {
+    upb_handlers_setbool(h, F(FieldOptions, lazy), &field_onlazy, NULL);
+    upb_handlers_setbool(h, F(FieldOptions, packed), &field_onpacked, NULL);
+  } else if (upbdefs_google_protobuf_MessageOptions_is(m)) {
+    upb_handlers_setbool(h, F(MessageOptions, map_entry), &msg_onmapentry, NULL);
   }
+
+  UPB_ASSERT(upb_ok(upb_handlers_status(h)));
 }
 
-#undef D
+#undef F
 
 void descreader_cleanup(void *_r) {
   upb_descreader *r = _r;
-  free(r->name);
-  upb_deflist_uninit(&r->defs);
-  free(r->default_string);
+  size_t i;
+
+  for (i = 0; i < upb_descreader_filecount(r); i++) {
+    upb_filedef_unref(upb_descreader_file(r, i), &r->files);
+  }
+
+  upb_gfree(r->name);
+  upb_inttable_uninit(&r->files);
+  upb_inttable_uninit(&r->oneofs);
+  upb_gfree(r->default_string);
   while (r->stack_len > 0) {
     upb_descreader_frame *f = &r->stack[--r->stack_len];
-    free(f->name);
+    upb_gfree(f->name);
   }
 }
 
@@ -6445,7 +7215,8 @@
     return NULL;
   }
 
-  upb_deflist_init(&r->defs);
+  upb_inttable_init(&r->files, UPB_CTYPE_PTR);
+  upb_inttable_init(&r->oneofs, UPB_CTYPE_PTR);
   upb_sink_reset(upb_descreader_input(r), h, r);
   r->stack_len = 0;
   r->name = NULL;
@@ -6454,10 +7225,17 @@
   return r;
 }
 
-upb_def **upb_descreader_getdefs(upb_descreader *r, void *owner, int *n) {
-  *n = r->defs.len;
-  upb_deflist_donaterefs(&r->defs, owner);
-  return r->defs.defs;
+size_t upb_descreader_filecount(const upb_descreader *r) {
+  return upb_inttable_count(&r->files);
+}
+
+upb_filedef *upb_descreader_file(const upb_descreader *r, size_t i) {
+  upb_value v;
+  if (upb_inttable_lookup(&r->files, i, &v)) {
+    return upb_value_getptr(v);
+  } else {
+    return NULL;
+  }
 }
 
 upb_sink *upb_descreader_input(upb_descreader *r) {
@@ -6465,10 +7243,9 @@
 }
 
 const upb_handlers *upb_descreader_newhandlers(const void *owner) {
-  const upb_symtab *s = upbdefs_google_protobuf_descriptor(&s);
-  const upb_handlers *h = upb_handlers_newfrozen(
-      upbdefs_google_protobuf_FileDescriptorSet(s), owner, reghandlers, s);
-  upb_symtab_unref(s, &s);
+  const upb_msgdef *m = upbdefs_google_protobuf_FileDescriptorSet_get(&m);
+  const upb_handlers *h = upb_handlers_newfrozen(m, owner, reghandlers, NULL);
+  upb_msgdef_unref(m, &m);
   return h;
 }
 /*
@@ -6502,8 +7279,8 @@
 #ifdef UPB_USE_JIT_X64
   upb_pbdecoder_freejit(g);
 #endif
-  free(g->bytecode);
-  free(g);
+  upb_gfree(g->bytecode);
+  upb_gfree(g);
 }
 
 static void visitgroup(const upb_refcounted *r, upb_refcounted_visit *visit,
@@ -6518,7 +7295,7 @@
 }
 
 mgroup *newgroup(const void *owner) {
-  mgroup *g = malloc(sizeof(*g));
+  mgroup *g = upb_gmalloc(sizeof(*g));
   static const struct upb_refcounted_vtbl vtbl = {visitgroup, freegroup};
   upb_refcounted_init(mgroup_upcast_mutable(g), &vtbl, owner);
   upb_inttable_init(&g->methods, UPB_CTYPE_PTR);
@@ -6538,7 +7315,7 @@
   }
 
   upb_inttable_uninit(&method->dispatch);
-  free(method);
+  upb_gfree(method);
 }
 
 static void visitmethod(const upb_refcounted *r, upb_refcounted_visit *visit,
@@ -6550,7 +7327,7 @@
 static upb_pbdecodermethod *newmethod(const upb_handlers *dest_handlers,
                                       mgroup *group) {
   static const struct upb_refcounted_vtbl vtbl = {visitmethod, freemethod};
-  upb_pbdecodermethod *ret = malloc(sizeof(*ret));
+  upb_pbdecodermethod *ret = upb_gmalloc(sizeof(*ret));
   upb_refcounted_init(upb_pbdecodermethod_upcast_mutable(ret), &vtbl, &ret);
   upb_byteshandler_init(&ret->input_handler_);
 
@@ -6613,7 +7390,7 @@
 } compiler;
 
 static compiler *newcompiler(mgroup *group, bool lazy) {
-  compiler *ret = malloc(sizeof(*ret));
+  compiler *ret = upb_gmalloc(sizeof(*ret));
   int i;
 
   ret->group = group;
@@ -6626,7 +7403,7 @@
 }
 
 static void freecompiler(compiler *c) {
-  free(c);
+  upb_gfree(c);
 }
 
 const size_t ptr_words = sizeof(void*) / sizeof(uint32_t);
@@ -6654,7 +7431,7 @@
     case OP_TAGN:
       return false;
     default:
-      assert(false);
+      UPB_ASSERT(false);
       return false;
   }
 }
@@ -6673,7 +7450,7 @@
   } else {
     *instruction = (*instruction & ~0xff00) | ((ofs & 0xff) << 8);
   }
-  assert(getofs(*instruction) == ofs);  /* Would fail in cases of overflow. */
+  UPB_ASSERT(getofs(*instruction) == ofs);  /* Would fail in cases of overflow. */
 }
 
 static uint32_t pcofs(compiler *c) { return c->pc - c->group->bytecode; }
@@ -6685,7 +7462,7 @@
   int val;
   uint32_t *codep;
 
-  assert(label < MAXLABEL);
+  UPB_ASSERT(label < MAXLABEL);
   val = c->fwd_labels[label];
   codep = (val == EMPTYLABEL) ? NULL : c->group->bytecode + val;
   while (codep) {
@@ -6706,7 +7483,7 @@
  * The returned value is the offset that should be written into the instruction.
  */
 static int32_t labelref(compiler *c, int label) {
-  assert(label < MAXLABEL);
+  UPB_ASSERT(label < MAXLABEL);
   if (label == LABEL_DISPATCH) {
     /* No resolving required. */
     return 0;
@@ -6730,7 +7507,8 @@
     size_t oldsize = g->bytecode_end - g->bytecode;
     size_t newsize = UPB_MAX(oldsize * 2, 64);
     /* TODO(haberman): handle OOM. */
-    g->bytecode = realloc(g->bytecode, newsize * sizeof(uint32_t));
+    g->bytecode = upb_grealloc(g->bytecode, oldsize * sizeof(uint32_t),
+                                            newsize * sizeof(uint32_t));
     g->bytecode_end = g->bytecode + newsize;
     c->pc = g->bytecode + ofs;
   }
@@ -6805,7 +7583,7 @@
       int label = va_arg(ap, int);
       uint64_t tag = va_arg(ap, uint64_t);
       uint32_t instruction = op | (tag << 16);
-      assert(tag <= 0xffff);
+      UPB_ASSERT(tag <= 0xffff);
       setofs(&instruction, labelref(c, label));
       put32(c, instruction);
       break;
@@ -6942,7 +7720,7 @@
   uint32_t tag = (upb_fielddef_number(f) << 3) | wire_type;
   uint64_t encoded_tag = upb_vencode32(tag);
   /* No tag should be greater than 5 bytes. */
-  assert(encoded_tag <= 0xffffffffff);
+  UPB_ASSERT(encoded_tag <= 0xffffffffff);
   return encoded_tag;
 }
 
@@ -6965,7 +7743,7 @@
 static upb_selector_t getsel(const upb_fielddef *f, upb_handlertype_t type) {
   upb_selector_t selector;
   bool ok = upb_handlers_getselector(f, type, &selector);
-  UPB_ASSERT_VAR(ok, ok);
+  UPB_ASSERT(ok);
   return selector;
 }
 
@@ -6977,7 +7755,7 @@
   uint8_t wt1;
   uint8_t old_wt2;
   upb_pbdecoder_unpackdispatch(dispatch, &ofs, &wt1, &old_wt2);
-  assert(old_wt2 == NO_WIRE_TYPE);  /* wt2 should not be set yet. */
+  UPB_ASSERT(old_wt2 == NO_WIRE_TYPE);  /* wt2 should not be set yet. */
   return upb_pbdecoder_packdispatch(ofs, wt1, new_wt2);
 }
 
@@ -7067,7 +7845,12 @@
 
   if (!sub_m) {
     /* Don't emit any code for this field at all; it will be parsed as an
-     * unknown field. */
+     * unknown field.
+     *
+     * TODO(haberman): we should change this to parse it as a string field
+     * instead.  It will probably be faster, but more importantly, once we
+     * start vending unknown fields, a field shouldn't be treated as unknown
+     * just because it doesn't have subhandlers registered. */
     return;
   }
 
@@ -7174,7 +7957,7 @@
    * setting in the fielddef.  This will favor (in speed) whichever was
    * specified. */
 
-  assert((int)parse_type >= 0 && parse_type <= OP_MAX);
+  UPB_ASSERT((int)parse_type >= 0 && parse_type <= OP_MAX);
   sel = getsel(f, upb_handlers_getprimitivehandlertype(f));
   wire_type = upb_pb_native_wire_types[upb_fielddef_descriptortype(f)];
   if (upb_fielddef_isseq(f)) {
@@ -7216,7 +7999,7 @@
   upb_msg_field_iter i;
   upb_value val;
 
-  assert(method);
+  UPB_ASSERT(method);
 
   /* Clear all entries in the dispatch table. */
   upb_inttable_uninit(&method->dispatch);
@@ -7364,7 +8147,7 @@
   compiler *c;
 
   UPB_UNUSED(allowjit);
-  assert(upb_handlers_isfrozen(dest));
+  UPB_ASSERT(upb_handlers_isfrozen(dest));
 
   g = newgroup(owner);
   c = newcompiler(g, lazy);
@@ -7384,11 +8167,16 @@
 
 #ifdef UPB_DUMP_BYTECODE
   {
-    FILE *f = fopen("/tmp/upb-bytecode", "wb");
-    assert(f);
+    FILE *f = fopen("/tmp/upb-bytecode", "w");
+    UPB_ASSERT(f);
     dumpbc(g->bytecode, g->bytecode_end, stderr);
     dumpbc(g->bytecode, g->bytecode_end, f);
     fclose(f);
+
+    f = fopen("/tmp/upb-bytecode.bin", "wb");
+    UPB_ASSERT(f);
+    fwrite(g->bytecode, 1, g->bytecode_end - g->bytecode, f);
+    fclose(f);
   }
 #endif
 
@@ -7436,7 +8224,7 @@
   upb_inttable_push(&c->groups, upb_value_constptr(g));
 
   ok = upb_inttable_lookupptr(&g->methods, opts->handlers, &v);
-  UPB_ASSERT_VAR(ok, ok);
+  UPB_ASSERT(ok);
   return upb_value_getptr(v);
 }
 
@@ -7488,6 +8276,11 @@
 
 static opcode halt = OP_HALT;
 
+/* A dummy character we can point to when the user passes us a NULL buffer.
+ * We need this because in C (NULL + 0) and (NULL - NULL) are undefined
+ * behavior, which would invalidate functions like curbufleft(). */
+static const char dummy_char;
+
 /* Whether an op consumes any of the input buffer. */
 static bool consumes_input(opcode op) {
   switch (op) {
@@ -7564,7 +8357,7 @@
 /* How many bytes can be safely read from d->ptr without reading past end-of-buf
  * or past the current delimited end. */
 static size_t curbufleft(const upb_pbdecoder *d) {
-  assert(d->data_end >= d->ptr);
+  UPB_ASSERT(d->data_end >= d->ptr);
   return d->data_end - d->ptr;
 }
 
@@ -7585,7 +8378,7 @@
 
 /* Advances d->ptr. */
 static void advance(upb_pbdecoder *d, size_t len) {
-  assert(curbufleft(d) >= len);
+  UPB_ASSERT(curbufleft(d) >= len);
   d->ptr += len;
 }
 
@@ -7618,7 +8411,7 @@
 }
 
 static void advancetobuf(upb_pbdecoder *d, const char *buf, size_t len) {
-  assert(curbufleft(d) == 0);
+  UPB_ASSERT(curbufleft(d) == 0);
   d->bufstart_ofs += (d->end - d->buf);
   switchtobuf(d, buf, buf + len);
 }
@@ -7627,7 +8420,7 @@
   /* The assertion here is in the interests of efficiency, not correctness.
    * We are trying to ensure that we don't checkpoint() more often than
    * necessary. */
-  assert(d->checkpoint != d->ptr);
+  UPB_ASSERT(d->checkpoint != d->ptr);
   d->checkpoint = d->ptr;
 }
 
@@ -7638,12 +8431,12 @@
  * won't actually be read.
  */
 static int32_t skip(upb_pbdecoder *d, size_t bytes) {
-  assert(!in_residual_buf(d, d->ptr) || d->size_param == 0);
-  assert(d->skip == 0);
+  UPB_ASSERT(!in_residual_buf(d, d->ptr) || d->size_param == 0);
+  UPB_ASSERT(d->skip == 0);
   if (bytes > delim_remaining(d)) {
     seterr(d, "Skipped value extended beyond enclosing submessage.");
     return upb_pbdecoder_suspend(d);
-  } else if (bufleft(d) > bytes) {
+  } else if (bufleft(d) >= bytes) {
     /* Skipped data is all in current buffer, and more is still available. */
     advance(d, bytes);
     d->skip = 0;
@@ -7665,25 +8458,31 @@
                              size_t size, const upb_bufhandle *handle) {
   UPB_UNUSED(p);  /* Useless; just for the benefit of the JIT. */
 
-  d->buf_param = buf;
+  /* d->skip and d->residual_end could probably elegantly be represented
+   * as a single variable, to more easily represent this invariant. */
+  UPB_ASSERT(!(d->skip && d->residual_end > d->residual));
+
+  /* We need to remember the original size_param, so that the value we return
+   * is relative to it, even if we do some skipping first. */
   d->size_param = size;
   d->handle = handle;
 
-  if (d->residual_end > d->residual) {
-    /* We have residual bytes from the last buffer. */
-    assert(d->ptr == d->residual);
-  } else {
-    switchtobuf(d, buf, buf + size);
+  /* Have to handle this case specially (ie. not with skip()) because the user
+   * is allowed to pass a NULL buffer here, which won't allow us to safely
+   * calculate a d->end or use our normal functions like curbufleft(). */
+  if (d->skip && d->skip >= size) {
+    d->skip -= size;
+    d->bufstart_ofs += size;
+    buf = &dummy_char;
+    size = 0;
+
+    /* We can't just return now, because we might need to execute some ops
+     * like CHECKDELIM, which could call some callbacks and pop the stack. */
   }
 
-  d->checkpoint = d->ptr;
-
-  if (d->skip) {
-    size_t skip_bytes = d->skip;
-    d->skip = 0;
-    CHECK_RETURN(skip(d, skip_bytes));
-    d->checkpoint = d->ptr;
-  }
+  /* We need to pretend that this was the actual buffer param, since some of the
+   * calculations assume that d->ptr/d->buf is relative to this. */
+  d->buf_param = buf;
 
   if (!buf) {
     /* NULL buf is ok if its entire span is covered by the "skip" above, but
@@ -7692,9 +8491,27 @@
     return upb_pbdecoder_suspend(d);
   }
 
+  if (d->residual_end > d->residual) {
+    /* We have residual bytes from the last buffer. */
+    UPB_ASSERT(d->ptr == d->residual);
+  } else {
+    switchtobuf(d, buf, buf + size);
+  }
+
+  d->checkpoint = d->ptr;
+
+  /* Handle skips that don't cover the whole buffer (as above). */
+  if (d->skip) {
+    size_t skip_bytes = d->skip;
+    d->skip = 0;
+    CHECK_RETURN(skip(d, skip_bytes));
+    checkpoint(d);
+  }
+
+  /* If we're inside an unknown group, continue to parse unknown values. */
   if (d->top->groupnum < 0) {
     CHECK_RETURN(upb_pbdecoder_skipunknown(d, -1, 0));
-    d->checkpoint = d->ptr;
+    checkpoint(d);
   }
 
   return DECODE_OK;
@@ -7709,15 +8526,14 @@
     d->ptr = d->residual;
     return 0;
   } else {
-    size_t consumed;
-    assert(!in_residual_buf(d, d->checkpoint));
-    assert(d->buf == d->buf_param);
+    size_t ret = d->size_param - (d->end - d->checkpoint);
+    UPB_ASSERT(!in_residual_buf(d, d->checkpoint));
+    UPB_ASSERT(d->buf == d->buf_param || d->buf == &dummy_char);
 
-    consumed = d->checkpoint - d->buf;
-    d->bufstart_ofs += consumed;
+    d->bufstart_ofs += (d->checkpoint - d->buf);
     d->residual_end = d->residual;
     switchtobuf(d, d->residual, d->residual_end);
-    return consumed;
+    return ret;
   }
 }
 
@@ -7732,7 +8548,7 @@
 
   if (d->checkpoint == d->residual) {
     /* Checkpoint was in residual buf; append user byte(s) to residual buf. */
-    assert((d->residual_end - d->residual) + d->size_param <=
+    UPB_ASSERT((d->residual_end - d->residual) + d->size_param <=
            sizeof(d->residual));
     if (!in_residual_buf(d, d->ptr)) {
       d->bufstart_ofs -= (d->residual_end - d->residual);
@@ -7742,11 +8558,11 @@
   } else {
     /* Checkpoint was in user buf; old residual bytes not needed. */
     size_t save;
-    assert(!in_residual_buf(d, d->checkpoint));
+    UPB_ASSERT(!in_residual_buf(d, d->checkpoint));
 
     d->ptr = d->checkpoint;
     save = curbufleft(d);
-    assert(save <= sizeof(d->residual));
+    UPB_ASSERT(save <= sizeof(d->residual));
     memcpy(d->residual, d->ptr, save);
     d->residual_end = d->residual + save;
     d->bufstart_ofs = offset(d);
@@ -7760,7 +8576,7 @@
  * Requires that this many bytes are available in the current buffer. */
 UPB_FORCEINLINE static void consumebytes(upb_pbdecoder *d, void *buf,
                                          size_t bytes) {
-  assert(bytes <= curbufleft(d));
+  UPB_ASSERT(bytes <= curbufleft(d));
   memcpy(buf, d->ptr, bytes);
   advance(d, bytes);
 }
@@ -7773,7 +8589,7 @@
   const size_t avail = curbufleft(d);
   consumebytes(d, buf, avail);
   bytes -= avail;
-  assert(bytes > 0);
+  UPB_ASSERT(bytes > 0);
   if (in_residual_buf(d, d->ptr)) {
     advancetobuf(d, d->buf_param, d->size_param);
   }
@@ -7835,8 +8651,7 @@
   int bitpos;
   *u64 = 0;
   for(bitpos = 0; bitpos < 70 && (byte & 0x80); bitpos += 7) {
-    int32_t ret = getbytes(d, &byte, 1);
-    if (ret >= 0) return ret;
+    CHECK_RETURN(getbytes(d, &byte, 1));
     *u64 |= (uint64_t)(byte & 0x7F) << bitpos;
   }
   if(bitpos == 70 && (byte & 0x80)) {
@@ -7957,7 +8772,7 @@
   if (read == bytes && data == expected) {
     /* Advance past matched bytes. */
     int32_t ok = getbytes(d, &data, read);
-    UPB_ASSERT_VAR(ok, ok < 0);
+    UPB_ASSERT(ok < 0);
     return DECODE_OK;
   } else if (read < bytes && memcmp(&data, &expected, read) == 0) {
     return suspend_save(d);
@@ -8032,7 +8847,7 @@
 static void goto_endmsg(upb_pbdecoder *d) {
   upb_value v;
   bool found = upb_inttable_lookup32(d->top->dispatch, DISPATCH_ENDMSG, &v);
-  UPB_ASSERT_VAR(found, found);
+  UPB_ASSERT(found);
   d->pc = d->top->base + upb_value_getuint64(v);
 }
 
@@ -8066,7 +8881,7 @@
     } else if (wire_type == ((v >> 8) & 0xff)) {
       bool found =
           upb_inttable_lookup(dispatch, fieldnum + UPB_MAX_FIELDNUMBER, &val);
-      UPB_ASSERT_VAR(found, found);
+      UPB_ASSERT(found);
       d->pc = d->top->base + upb_value_getuint64(val);
       return DECODE_OK;
     }
@@ -8078,7 +8893,7 @@
    * can re-check the delimited end. */
   d->last--;  /* Necessary if we get suspended */
   d->pc = d->last;
-  assert(getop(*d->last) == OP_CHECKDELIM);
+  UPB_ASSERT(getop(*d->last) == OP_CHECKDELIM);
 
   /* Unknown field or ENDGROUP. */
   retval = upb_pbdecoder_skipunknown(d, fieldnum, wire_type);
@@ -8096,7 +8911,7 @@
 /* Callers know that the stack is more than one deep because the opcodes that
  * call this only occur after PUSH operations. */
 upb_pbdecoder_frame *outer_frame(upb_pbdecoder *d) {
-  assert(d->top != d->stack);
+  UPB_ASSERT(d->top != d->stack);
   return d->top - 1;
 }
 
@@ -8128,7 +8943,7 @@
     op = getop(instruction);
     arg = instruction >> 8;
     longofs = arg;
-    assert(d->ptr != d->residual_end);
+    UPB_ASSERT(d->ptr != d->residual_end);
     UPB_UNUSED(group);
 #ifdef UPB_DUMP_BYTECODE
     fprintf(stderr, "s_ofs=%d buf_ofs=%d data_rem=%d buf_rem=%d delim_rem=%d "
@@ -8203,7 +9018,7 @@
           } else {
             int32_t ret = skip(d, n);
             /* This shouldn't return DECODE_OK, because n > len. */
-            assert(ret >= 0);
+            UPB_ASSERT(ret >= 0);
             return ret;
           }
         }
@@ -8225,7 +9040,7 @@
         d->top->groupnum = *d->pc++;
       )
       VMCASE(OP_POP,
-        assert(d->top > d->stack);
+        UPB_ASSERT(d->top > d->stack);
         decoder_pop(d);
       )
       VMCASE(OP_PUSHLENDELIM,
@@ -8241,7 +9056,7 @@
         /* We are guaranteed of this assert because we never allow ourselves to
          * consume bytes beyond data_end, which covers delim_end when non-NULL.
          */
-        assert(!(d->delim_end && d->ptr > d->delim_end));
+        UPB_ASSERT(!(d->delim_end && d->ptr > d->delim_end));
         if (d->ptr == d->delim_end)
           d->pc += longofs;
       )
@@ -8250,7 +9065,7 @@
         d->pc += longofs;
       )
       VMCASE(OP_RET,
-        assert(d->call_len > 0);
+        UPB_ASSERT(d->call_len > 0);
         d->pc = d->callstack[--d->call_len];
       )
       VMCASE(OP_BRANCH,
@@ -8377,7 +9192,7 @@
     if (p != method->code_base.ptr) p--;
     if (getop(*p) == OP_CHECKDELIM) {
       /* Rewind from OP_TAG* to OP_CHECKDELIM. */
-      assert(getop(*d->pc) == OP_TAG1 ||
+      UPB_ASSERT(getop(*d->pc) == OP_TAG1 ||
              getop(*d->pc) == OP_TAG2 ||
              getop(*d->pc) == OP_TAGN ||
              getop(*d->pc) == OP_DISPATCH);
@@ -8436,11 +9251,12 @@
   d->env = e;
   d->limit = d->stack + default_max_nesting - 1;
   d->stack_size = default_max_nesting;
+  d->status = NULL;
 
   upb_pbdecoder_reset(d);
   upb_bytessink_reset(&d->input_, &m->input_handler_, d);
 
-  assert(sink);
+  UPB_ASSERT(sink);
   if (d->method_->dest_handlers_) {
     if (sink->handlers != d->method_->dest_handlers_)
       return NULL;
@@ -8448,7 +9264,8 @@
   upb_sink_reset(&d->top->sink, sink->handlers, sink->closure);
 
   /* If this fails, increase the value in decoder.h. */
-  assert(upb_env_bytesallocated(e) - size_before <= UPB_PB_DECODER_SIZE);
+  UPB_ASSERT_DEBUGVAR(upb_env_bytesallocated(e) - size_before <=
+                      UPB_PB_DECODER_SIZE);
   return d;
 }
 
@@ -8469,7 +9286,7 @@
 }
 
 bool upb_pbdecoder_setmaxnesting(upb_pbdecoder *d, size_t max) {
-  assert(d->top >= d->stack);
+  UPB_ASSERT(d->top >= d->stack);
 
   if (max < (size_t)(d->top - d->stack)) {
     /* Can't set a limit smaller than what we are currently at. */
@@ -8557,7 +9374,6 @@
 */
 
 
-#include <stdlib.h>
 
 /* The output buffer is divided into segments; a segment is a string of data
  * that is "ready to go" -- it does not need any varint lengths inserted into
@@ -8629,7 +9445,7 @@
 /* TODO(haberman): handle pushback */
 static void putbuf(upb_pb_encoder *e, const char *buf, size_t len) {
   size_t n = upb_bytessink_putbuf(e->output_, e->subc, buf, len, NULL);
-  UPB_ASSERT_VAR(n, n == len);
+  UPB_ASSERT(n == len);
 }
 
 static upb_pb_encoder_segment *top(upb_pb_encoder *e) {
@@ -8669,7 +9485,7 @@
 /* Call when "bytes" bytes have been writte at e->ptr.  The caller *must* have
  * previously called reserve() with at least this many bytes. */
 static void encoder_advance(upb_pb_encoder *e, size_t bytes) {
-  assert((size_t)(e->limit - e->ptr) >= bytes);
+  UPB_ASSERT((size_t)(e->limit - e->ptr) >= bytes);
   e->ptr += bytes;
 }
 
@@ -8704,7 +9520,7 @@
  * length. */
 static void accumulate(upb_pb_encoder *e) {
   size_t run_len;
-  assert(e->ptr >= e->runbegin);
+  UPB_ASSERT(e->ptr >= e->runbegin);
   run_len = e->ptr - e->runbegin;
   e->segptr->seglen += run_len;
   top(e)->msglen += run_len;
@@ -8802,12 +9618,12 @@
                     upb_handlerattr *attr) {
   uint32_t n = upb_fielddef_number(f);
 
-  tag_t *tag = malloc(sizeof(tag_t));
+  tag_t *tag = upb_gmalloc(sizeof(tag_t));
   tag->bytes = upb_vencode64((n << 3) | wt, tag->tag);
 
   upb_handlerattr_init(attr);
   upb_handlerattr_sethandlerdata(attr, tag);
-  upb_handlers_addcleanup(h, tag, free);
+  upb_handlers_addcleanup(h, tag, upb_gfree);
 }
 
 static bool encode_tag(upb_pb_encoder *e, const tag_t *tag) {
@@ -9059,19 +9875,17 @@
   e->ptr = e->buf;
 
   /* If this fails, increase the value in encoder.h. */
-  assert(upb_env_bytesallocated(env) - size_before <= UPB_PB_ENCODER_SIZE);
+  UPB_ASSERT_DEBUGVAR(upb_env_bytesallocated(env) - size_before <=
+                      UPB_PB_ENCODER_SIZE);
   return e;
 }
 
 upb_sink *upb_pb_encoder_input(upb_pb_encoder *e) { return &e->input_; }
 
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
 
-upb_def **upb_load_defs_from_descriptor(const char *str, size_t len, int *n,
-                                        void *owner, upb_status *status) {
+upb_filedef **upb_loaddescriptor(const char *buf, size_t n, const void *owner,
+                                 upb_status *status) {
   /* Create handlers. */
   const upb_pbdecodermethod *decoder_m;
   const upb_handlers *reader_h = upb_descreader_newhandlers(&reader_h);
@@ -9080,8 +9894,8 @@
   upb_pbdecoder *decoder;
   upb_descreader *reader;
   bool ok;
-  upb_def **ret = NULL;
-  upb_def **defs;
+  size_t i;
+  upb_filedef **ret = NULL;
 
   upb_pbdecodermethodopts_init(&opts, reader_h);
   decoder_m = upb_pbdecodermethod_new(&opts, &decoder_m);
@@ -9093,12 +9907,24 @@
   decoder = upb_pbdecoder_create(&env, decoder_m, upb_descreader_input(reader));
 
   /* Push input data. */
-  ok = upb_bufsrc_putbuf(str, len, upb_pbdecoder_input(decoder));
+  ok = upb_bufsrc_putbuf(buf, n, upb_pbdecoder_input(decoder));
 
-  if (!ok) goto cleanup;
-  defs = upb_descreader_getdefs(reader, owner, n);
-  ret = malloc(sizeof(upb_def*) * (*n));
-  memcpy(ret, defs, sizeof(upb_def*) * (*n));
+  if (!ok) {
+    goto cleanup;
+  }
+
+  ret = upb_gmalloc(sizeof (*ret) * (upb_descreader_filecount(reader) + 1));
+
+  if (!ret) {
+    goto cleanup;
+  }
+
+  for (i = 0; i < upb_descreader_filecount(reader); i++) {
+    ret[i] = upb_descreader_file(reader, i);
+    upb_filedef_ref(ret[i], owner);
+  }
+
+  ret[i] = NULL;
 
 cleanup:
   upb_env_uninit(&env);
@@ -9106,51 +9932,6 @@
   upb_pbdecodermethod_unref(decoder_m, &decoder_m);
   return ret;
 }
-
-bool upb_load_descriptor_into_symtab(upb_symtab *s, const char *str, size_t len,
-                                     upb_status *status) {
-  int n;
-  bool success;
-  upb_def **defs = upb_load_defs_from_descriptor(str, len, &n, &defs, status);
-  if (!defs) return false;
-  success = upb_symtab_add(s, defs, n, &defs, status);
-  free(defs);
-  return success;
-}
-
-char *upb_readfile(const char *filename, size_t *len) {
-  long size;
-  char *buf;
-  FILE *f = fopen(filename, "rb");
-  if(!f) return NULL;
-  if(fseek(f, 0, SEEK_END) != 0) goto error;
-  size = ftell(f);
-  if(size < 0) goto error;
-  if(fseek(f, 0, SEEK_SET) != 0) goto error;
-  buf = malloc(size + 1);
-  if(size && fread(buf, size, 1, f) != 1) goto error;
-  fclose(f);
-  if (len) *len = size;
-  return buf;
-
-error:
-  fclose(f);
-  return NULL;
-}
-
-bool upb_load_descriptor_file_into_symtab(upb_symtab *symtab, const char *fname,
-                                          upb_status *status) {
-  size_t len;
-  bool success;
-  char *data = upb_readfile(fname, &len);
-  if (!data) {
-    if (status) upb_status_seterrf(status, "Couldn't read file: %s", fname);
-    return false;
-  }
-  success = upb_load_descriptor_into_symtab(symtab, data, len, status);
-  free(data);
-  return success;
-}
 /*
  * upb::pb::TextPrinter
  *
@@ -9164,7 +9945,6 @@
 #include <inttypes.h>
 #include <stdarg.h>
 #include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
 
 
@@ -9260,14 +10040,14 @@
   va_end(args_copy);
 
   /* + 1 for NULL terminator (vsprintf() requires it even if we don't). */
-  str = malloc(len + 1);
+  str = upb_gmalloc(len + 1);
   if (!str) return false;
   written = vsprintf(str, fmt, args);
   va_end(args);
-  UPB_ASSERT_VAR(written, written == len);
+  UPB_ASSERT(written == len);
 
   ok = upb_bytessink_putbuf(p->output_, p->subc, str, len, NULL);
-  free(str);
+  upb_gfree(str);
   return ok;
 }
 
@@ -9636,12 +10416,11 @@
 ** - handling of keys/escape-sequences/etc that span input buffers.
 */
 
-#include <stdio.h>
-#include <stdint.h>
 #include <assert.h>
-#include <string.h>
-#include <stdlib.h>
 #include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
 
 
 #define UPB_JSON_MAX_DEPTH 64
@@ -9654,6 +10433,9 @@
   const upb_msgdef *m;
   const upb_fielddef *f;
 
+  /* The table mapping json name to fielddef for this message. */
+  upb_strtable *name_table;
+
   /* We are in a repeated-field context, ready to emit mapentries as
    * submessages. This flag alters the start-of-object (open-brace) behavior to
    * begin a sequence of mapentry messages rather than a single submessage. */
@@ -9674,7 +10456,7 @@
 
 struct upb_json_parser {
   upb_env *env;
-  upb_byteshandler input_handler_;
+  const upb_json_parsermethod *method;
   upb_bytessink input_;
 
   /* Stack to track the JSON scopes we are in. */
@@ -9709,6 +10491,19 @@
   uint32_t digit;
 };
 
+struct upb_json_parsermethod {
+  upb_refcounted base;
+
+  upb_byteshandler input_handler_;
+
+  /* Mainly for the purposes of refcounting, so all the fielddefs we point
+   * to stay alive. */
+  const upb_msgdef *msg;
+
+  /* Keys are upb_msgdef*, values are upb_strtable (json_name -> fielddef) */
+  upb_inttable name_tables;
+};
+
 #define PARSER_CHECK_RETURN(x) if (!(x)) return false
 
 /* Used to signal that a capture has been suspended. */
@@ -9718,7 +10513,7 @@
                                              upb_handlertype_t type) {
   upb_selector_t sel;
   bool ok = upb_handlers_getselector(p->top->f, type, &sel);
-  UPB_ASSERT_VAR(ok, ok);
+  UPB_ASSERT(ok);
   return sel;
 }
 
@@ -9737,6 +10532,13 @@
   return true;
 }
 
+static void set_name_table(upb_json_parser *p, upb_jsonparser_frame *frame) {
+  upb_value v;
+  bool ok = upb_inttable_lookupptr(&p->method->name_tables, frame->m, &v);
+  UPB_ASSERT(ok);
+  frame->name_table = upb_value_getptr(v);
+}
+
 /* There are GCC/Clang built-ins for overflow checking which we could start
  * using if there was any performance benefit to it. */
 
@@ -9856,7 +10658,7 @@
     val = b64lookup(ptr[0]) << 18 |
           b64lookup(ptr[1]) << 12;
 
-    assert(!(val & 0x80000000));
+    UPB_ASSERT(!(val & 0x80000000));
     output = val >> 16;
     upb_sink_putstring(&p->top->sink, sel, &output, 1, NULL);
     return true;
@@ -9910,9 +10712,8 @@
  *      the true value in a contiguous buffer. */
 
 static void assert_accumulate_empty(upb_json_parser *p) {
-  UPB_UNUSED(p);
-  assert(p->accumulated == NULL);
-  assert(p->accumulated_len == 0);
+  UPB_ASSERT(p->accumulated == NULL);
+  UPB_ASSERT(p->accumulated_len == 0);
 }
 
 static void accumulate_clear(upb_json_parser *p) {
@@ -9978,7 +10779,7 @@
  * call, and writes the length to *len.  This with point either to the input
  * buffer or a temporary accumulate buffer. */
 static const char *accumulate_getptr(upb_json_parser *p, size_t *len) {
-  assert(p->accumulated);
+  UPB_ASSERT(p->accumulated);
   *len = p->accumulated_len;
   return p->accumulated;
 }
@@ -10016,7 +10817,7 @@
  * the end. */
 static void multipart_startaccum(upb_json_parser *p) {
   assert_accumulate_empty(p);
-  assert(p->multipart_state == MULTIPART_INACTIVE);
+  UPB_ASSERT(p->multipart_state == MULTIPART_INACTIVE);
   p->multipart_state = MULTIPART_ACCUMULATE;
 }
 
@@ -10024,7 +10825,7 @@
  * value with the given selector. */
 static void multipart_start(upb_json_parser *p, upb_selector_t sel) {
   assert_accumulate_empty(p);
-  assert(p->multipart_state == MULTIPART_INACTIVE);
+  UPB_ASSERT(p->multipart_state == MULTIPART_INACTIVE);
   p->multipart_state = MULTIPART_PUSHEAGERLY;
   p->string_selector = sel;
 }
@@ -10057,7 +10858,7 @@
 /* Note: this invalidates the accumulate buffer!  Call only after reading its
  * contents. */
 static void multipart_end(upb_json_parser *p) {
-  assert(p->multipart_state != MULTIPART_INACTIVE);
+  UPB_ASSERT(p->multipart_state != MULTIPART_INACTIVE);
   p->multipart_state = MULTIPART_INACTIVE;
   accumulate_clear(p);
 }
@@ -10070,13 +10871,13 @@
  * region. */
 
 static void capture_begin(upb_json_parser *p, const char *ptr) {
-  assert(p->multipart_state != MULTIPART_INACTIVE);
-  assert(p->capture == NULL);
+  UPB_ASSERT(p->multipart_state != MULTIPART_INACTIVE);
+  UPB_ASSERT(p->capture == NULL);
   p->capture = ptr;
 }
 
 static bool capture_end(upb_json_parser *p, const char *ptr) {
-  assert(p->capture);
+  UPB_ASSERT(p->capture);
   if (multipart_text(p, p->capture, ptr - p->capture, true)) {
     p->capture = NULL;
     return true;
@@ -10109,7 +10910,7 @@
 
 static void capture_resume(upb_json_parser *p, const char *ptr) {
   if (p->capture) {
-    assert(p->capture == &suspend_capture);
+    UPB_ASSERT(p->capture == &suspend_capture);
     p->capture = ptr;
   }
 }
@@ -10131,7 +10932,7 @@
     case '"': return '"';
     case '\\': return '\\';
     default:
-      assert(0);
+      UPB_ASSERT(0);
       return 'x';
   }
 }
@@ -10155,7 +10956,7 @@
   } else if (ch >= 'a' && ch <= 'f') {
     p->digit += ((ch - 'a') + 10);
   } else {
-    assert(ch >= 'A' && ch <= 'F');
+    UPB_ASSERT(ch >= 'A' && ch <= 'F');
     p->digit += ((ch - 'A') + 10);
   }
 }
@@ -10286,7 +11087,7 @@
       break;
     }
     default:
-      assert(false);
+      UPB_ASSERT(false);
   }
 
   multipart_end(p);
@@ -10312,13 +11113,13 @@
   }
 
   ok = upb_sink_putbool(&p->top->sink, parser_getsel(p), val);
-  UPB_ASSERT_VAR(ok, ok);
+  UPB_ASSERT(ok);
 
   return true;
 }
 
 static bool start_stringval(upb_json_parser *p) {
-  assert(p->top->f);
+  UPB_ASSERT(p->top->f);
 
   if (upb_fielddef_isstring(p->top->f)) {
     upb_jsonparser_frame *inner;
@@ -10333,6 +11134,7 @@
     upb_sink_startstr(&p->top->sink, sel, 0, &inner->sink);
     inner->m = p->top->m;
     inner->f = p->top->f;
+    inner->name_table = NULL;
     inner->is_map = false;
     inner->is_mapentry = false;
     p->top = inner;
@@ -10379,8 +11181,8 @@
 
     case UPB_TYPE_STRING: {
       upb_selector_t sel = getsel_for_handlertype(p, UPB_HANDLER_ENDSTR);
-      upb_sink_endstr(&p->top->sink, sel);
       p->top--;
+      upb_sink_endstr(&p->top->sink, sel);
       break;
     }
 
@@ -10407,7 +11209,7 @@
     }
 
     default:
-      assert(false);
+      UPB_ASSERT(false);
       upb_status_seterrmsg(&p->status, "Internal error in JSON decoder");
       upb_env_reporterror(p->env, &p->status);
       ok = false;
@@ -10420,7 +11222,7 @@
 }
 
 static void start_member(upb_json_parser *p) {
-  assert(!p->top->f);
+  UPB_ASSERT(!p->top->f);
   multipart_startaccum(p);
 }
 
@@ -10478,7 +11280,7 @@
       sel = getsel_for_handlertype(p, UPB_HANDLER_STRING);
       upb_sink_putstring(&subsink, sel, buf, len, NULL);
       sel = getsel_for_handlertype(p, UPB_HANDLER_ENDSTR);
-      upb_sink_endstr(&subsink, sel);
+      upb_sink_endstr(&p->top->sink, sel);
       multipart_end(p);
       break;
     }
@@ -10519,6 +11321,7 @@
   sel = getsel_for_handlertype(p, UPB_HANDLER_STARTSUBMSG);
   upb_sink_startsubmsg(&p->top->sink, sel, &inner->sink);
   inner->m = mapentrymsg;
+  inner->name_table = NULL;
   inner->mapfield = mapfield;
   inner->is_map = false;
 
@@ -10548,27 +11351,27 @@
 }
 
 static bool end_membername(upb_json_parser *p) {
-  assert(!p->top->f);
+  UPB_ASSERT(!p->top->f);
 
   if (p->top->is_map) {
     return handle_mapentry(p);
   } else {
     size_t len;
     const char *buf = accumulate_getptr(p, &len);
-    const upb_fielddef *f = upb_msgdef_ntof(p->top->m, buf, len);
+    upb_value v;
 
-    if (!f) {
+    if (upb_strtable_lookup2(p->top->name_table, buf, len, &v)) {
+      p->top->f = upb_value_getconstptr(v);
+      multipart_end(p);
+
+      return true;
+    } else {
       /* TODO(haberman): Ignore unknown fields if requested/configured to do
        * so. */
       upb_status_seterrf(&p->status, "No such field: %.*s\n", (int)len, buf);
       upb_env_reporterror(p->env, &p->status);
       return false;
     }
-
-    p->top->f = f;
-    multipart_end(p);
-
-    return true;
   }
 }
 
@@ -10580,7 +11383,7 @@
     bool ok;
     const upb_fielddef *mapfield;
 
-    assert(p->top > p->stack);
+    UPB_ASSERT(p->top > p->stack);
     /* send ENDMSG on submsg. */
     upb_sink_endmsg(&p->top->sink, &s);
     mapfield = p->top->mapfield;
@@ -10588,7 +11391,7 @@
     /* send ENDSUBMSG in repeated-field-of-mapentries frame. */
     p->top--;
     ok = upb_handlers_getselector(mapfield, UPB_HANDLER_ENDSUBMSG, &sel);
-    UPB_ASSERT_VAR(ok, ok);
+    UPB_ASSERT(ok);
     upb_sink_endsubmsg(&p->top->sink, sel);
   }
 
@@ -10596,7 +11399,7 @@
 }
 
 static bool start_subobject(upb_json_parser *p) {
-  assert(p->top->f);
+  UPB_ASSERT(p->top->f);
 
   if (upb_fielddef_ismap(p->top->f)) {
     upb_jsonparser_frame *inner;
@@ -10610,6 +11413,7 @@
     sel = getsel_for_handlertype(p, UPB_HANDLER_STARTSEQ);
     upb_sink_startseq(&p->top->sink, sel, &inner->sink);
     inner->m = upb_fielddef_msgsubdef(p->top->f);
+    inner->name_table = NULL;
     inner->mapfield = p->top->f;
     inner->f = NULL;
     inner->is_map = true;
@@ -10630,6 +11434,7 @@
     sel = getsel_for_handlertype(p, UPB_HANDLER_STARTSUBMSG);
     upb_sink_startsubmsg(&p->top->sink, sel, &inner->sink);
     inner->m = upb_fielddef_msgsubdef(p->top->f);
+    set_name_table(p, inner);
     inner->f = NULL;
     inner->is_map = false;
     inner->is_mapentry = false;
@@ -10663,7 +11468,7 @@
   upb_jsonparser_frame *inner;
   upb_selector_t sel;
 
-  assert(p->top->f);
+  UPB_ASSERT(p->top->f);
 
   if (!upb_fielddef_isseq(p->top->f)) {
     upb_status_seterrf(&p->status,
@@ -10679,6 +11484,7 @@
   sel = getsel_for_handlertype(p, UPB_HANDLER_STARTSEQ);
   upb_sink_startseq(&p->top->sink, sel, &inner->sink);
   inner->m = p->top->m;
+  inner->name_table = NULL;
   inner->f = p->top->f;
   inner->is_map = false;
   inner->is_mapentry = false;
@@ -10690,7 +11496,7 @@
 static void end_array(upb_json_parser *p) {
   upb_selector_t sel;
 
-  assert(p->top > p->stack);
+  UPB_ASSERT(p->top > p->stack);
 
   p->top--;
   sel = getsel_for_handlertype(p, UPB_HANDLER_ENDSEQ);
@@ -10736,11 +11542,11 @@
  * final state once, when the closing '"' is seen. */
 
 
-#line 1218 "upb/json/parser.rl"
+#line 1244 "upb/json/parser.rl"
 
 
 
-#line 1130 "upb/json/parser.c"
+#line 1156 "upb/json/parser.c"
 static const char _json_actions[] = {
 	0, 1, 0, 1, 2, 1, 3, 1, 
 	5, 1, 6, 1, 7, 1, 8, 1, 
@@ -10889,7 +11695,7 @@
 static const int json_en_main = 1;
 
 
-#line 1221 "upb/json/parser.rl"
+#line 1247 "upb/json/parser.rl"
 
 size_t parse(void *closure, const void *hd, const char *buf, size_t size,
              const upb_bufhandle *handle) {
@@ -10911,7 +11717,7 @@
   capture_resume(parser, buf);
 
   
-#line 1301 "upb/json/parser.c"
+#line 1327 "upb/json/parser.c"
 	{
 	int _klen;
 	unsigned int _trans;
@@ -10986,118 +11792,118 @@
 		switch ( *_acts++ )
 		{
 	case 0:
-#line 1133 "upb/json/parser.rl"
-	{ p--; {cs = stack[--top]; goto _again;} }
-	break;
-	case 1:
-#line 1134 "upb/json/parser.rl"
-	{ p--; {stack[top++] = cs; cs = 10; goto _again;} }
-	break;
-	case 2:
-#line 1138 "upb/json/parser.rl"
-	{ start_text(parser, p); }
-	break;
-	case 3:
-#line 1139 "upb/json/parser.rl"
-	{ CHECK_RETURN_TOP(end_text(parser, p)); }
-	break;
-	case 4:
-#line 1145 "upb/json/parser.rl"
-	{ start_hex(parser); }
-	break;
-	case 5:
-#line 1146 "upb/json/parser.rl"
-	{ hexdigit(parser, p); }
-	break;
-	case 6:
-#line 1147 "upb/json/parser.rl"
-	{ CHECK_RETURN_TOP(end_hex(parser)); }
-	break;
-	case 7:
-#line 1153 "upb/json/parser.rl"
-	{ CHECK_RETURN_TOP(escape(parser, p)); }
-	break;
-	case 8:
 #line 1159 "upb/json/parser.rl"
 	{ p--; {cs = stack[--top]; goto _again;} }
 	break;
+	case 1:
+#line 1160 "upb/json/parser.rl"
+	{ p--; {stack[top++] = cs; cs = 10; goto _again;} }
+	break;
+	case 2:
+#line 1164 "upb/json/parser.rl"
+	{ start_text(parser, p); }
+	break;
+	case 3:
+#line 1165 "upb/json/parser.rl"
+	{ CHECK_RETURN_TOP(end_text(parser, p)); }
+	break;
+	case 4:
+#line 1171 "upb/json/parser.rl"
+	{ start_hex(parser); }
+	break;
+	case 5:
+#line 1172 "upb/json/parser.rl"
+	{ hexdigit(parser, p); }
+	break;
+	case 6:
+#line 1173 "upb/json/parser.rl"
+	{ CHECK_RETURN_TOP(end_hex(parser)); }
+	break;
+	case 7:
+#line 1179 "upb/json/parser.rl"
+	{ CHECK_RETURN_TOP(escape(parser, p)); }
+	break;
+	case 8:
+#line 1185 "upb/json/parser.rl"
+	{ p--; {cs = stack[--top]; goto _again;} }
+	break;
 	case 9:
-#line 1162 "upb/json/parser.rl"
+#line 1188 "upb/json/parser.rl"
 	{ {stack[top++] = cs; cs = 19; goto _again;} }
 	break;
 	case 10:
-#line 1164 "upb/json/parser.rl"
+#line 1190 "upb/json/parser.rl"
 	{ p--; {stack[top++] = cs; cs = 27; goto _again;} }
 	break;
 	case 11:
-#line 1169 "upb/json/parser.rl"
+#line 1195 "upb/json/parser.rl"
 	{ start_member(parser); }
 	break;
 	case 12:
-#line 1170 "upb/json/parser.rl"
+#line 1196 "upb/json/parser.rl"
 	{ CHECK_RETURN_TOP(end_membername(parser)); }
 	break;
 	case 13:
-#line 1173 "upb/json/parser.rl"
+#line 1199 "upb/json/parser.rl"
 	{ end_member(parser); }
 	break;
 	case 14:
-#line 1179 "upb/json/parser.rl"
+#line 1205 "upb/json/parser.rl"
 	{ start_object(parser); }
 	break;
 	case 15:
-#line 1182 "upb/json/parser.rl"
+#line 1208 "upb/json/parser.rl"
 	{ end_object(parser); }
 	break;
 	case 16:
-#line 1188 "upb/json/parser.rl"
+#line 1214 "upb/json/parser.rl"
 	{ CHECK_RETURN_TOP(start_array(parser)); }
 	break;
 	case 17:
-#line 1192 "upb/json/parser.rl"
+#line 1218 "upb/json/parser.rl"
 	{ end_array(parser); }
 	break;
 	case 18:
-#line 1197 "upb/json/parser.rl"
+#line 1223 "upb/json/parser.rl"
 	{ start_number(parser, p); }
 	break;
 	case 19:
-#line 1198 "upb/json/parser.rl"
+#line 1224 "upb/json/parser.rl"
 	{ CHECK_RETURN_TOP(end_number(parser, p)); }
 	break;
 	case 20:
-#line 1200 "upb/json/parser.rl"
+#line 1226 "upb/json/parser.rl"
 	{ CHECK_RETURN_TOP(start_stringval(parser)); }
 	break;
 	case 21:
-#line 1201 "upb/json/parser.rl"
+#line 1227 "upb/json/parser.rl"
 	{ CHECK_RETURN_TOP(end_stringval(parser)); }
 	break;
 	case 22:
-#line 1203 "upb/json/parser.rl"
+#line 1229 "upb/json/parser.rl"
 	{ CHECK_RETURN_TOP(parser_putbool(parser, true)); }
 	break;
 	case 23:
-#line 1205 "upb/json/parser.rl"
+#line 1231 "upb/json/parser.rl"
 	{ CHECK_RETURN_TOP(parser_putbool(parser, false)); }
 	break;
 	case 24:
-#line 1207 "upb/json/parser.rl"
+#line 1233 "upb/json/parser.rl"
 	{ /* null value */ }
 	break;
 	case 25:
-#line 1209 "upb/json/parser.rl"
+#line 1235 "upb/json/parser.rl"
 	{ CHECK_RETURN_TOP(start_subobject(parser)); }
 	break;
 	case 26:
-#line 1210 "upb/json/parser.rl"
+#line 1236 "upb/json/parser.rl"
 	{ end_subobject(parser); }
 	break;
 	case 27:
-#line 1215 "upb/json/parser.rl"
+#line 1241 "upb/json/parser.rl"
 	{ p--; {cs = stack[--top]; goto _again;} }
 	break;
-#line 1487 "upb/json/parser.c"
+#line 1513 "upb/json/parser.c"
 		}
 	}
 
@@ -11110,10 +11916,10 @@
 	_out: {}
 	}
 
-#line 1242 "upb/json/parser.rl"
+#line 1268 "upb/json/parser.rl"
 
   if (p != pe) {
-    upb_status_seterrf(&parser->status, "Parse error at %s\n", p);
+    upb_status_seterrf(&parser->status, "Parse error at '%.*s'\n", pe - p, p);
     upb_env_reporterror(parser->env, &parser->status);
   } else {
     capture_suspend(parser, &p);
@@ -11151,13 +11957,13 @@
 
   /* Emit Ragel initialization of the parser. */
   
-#line 1541 "upb/json/parser.c"
+#line 1567 "upb/json/parser.c"
 	{
 	cs = json_start;
 	top = 0;
 	}
 
-#line 1282 "upb/json/parser.rl"
+#line 1308 "upb/json/parser.rl"
   p->current_state = cs;
   p->parser_top = top;
   accumulate_clear(p);
@@ -11167,10 +11973,84 @@
   upb_status_clear(&p->status);
 }
 
+static void visit_json_parsermethod(const upb_refcounted *r,
+                                    upb_refcounted_visit *visit,
+                                    void *closure) {
+  const upb_json_parsermethod *method = (upb_json_parsermethod*)r;
+  visit(r, upb_msgdef_upcast2(method->msg), closure);
+}
+
+static void free_json_parsermethod(upb_refcounted *r) {
+  upb_json_parsermethod *method = (upb_json_parsermethod*)r;
+
+  upb_inttable_iter i;
+  upb_inttable_begin(&i, &method->name_tables);
+  for(; !upb_inttable_done(&i); upb_inttable_next(&i)) {
+    upb_value val = upb_inttable_iter_value(&i);
+    upb_strtable *t = upb_value_getptr(val);
+    upb_strtable_uninit(t);
+    upb_gfree(t);
+  }
+
+  upb_inttable_uninit(&method->name_tables);
+
+  upb_gfree(r);
+}
+
+static void add_jsonname_table(upb_json_parsermethod *m, const upb_msgdef* md) {
+  upb_msg_field_iter i;
+  upb_strtable *t;
+
+  /* It would be nice to stack-allocate this, but protobufs do not limit the
+   * length of fields to any reasonable limit. */
+  char *buf = NULL;
+  size_t len = 0;
+
+  if (upb_inttable_lookupptr(&m->name_tables, md, NULL)) {
+    return;
+  }
+
+  /* TODO(haberman): handle malloc failure. */
+  t = upb_gmalloc(sizeof(*t));
+  upb_strtable_init(t, UPB_CTYPE_CONSTPTR);
+  upb_inttable_insertptr(&m->name_tables, md, upb_value_ptr(t));
+
+  for(upb_msg_field_begin(&i, md);
+      !upb_msg_field_done(&i);
+      upb_msg_field_next(&i)) {
+    const upb_fielddef *f = upb_msg_iter_field(&i);
+
+    /* Add an entry for the JSON name. */
+    size_t field_len = upb_fielddef_getjsonname(f, buf, len);
+    if (field_len > len) {
+      size_t len2;
+      buf = upb_grealloc(buf, 0, field_len);
+      len = field_len;
+      len2 = upb_fielddef_getjsonname(f, buf, len);
+      UPB_ASSERT(len == len2);
+    }
+    upb_strtable_insert(t, buf, upb_value_constptr(f));
+
+    if (strcmp(buf, upb_fielddef_name(f)) != 0) {
+      /* Since the JSON name is different from the regular field name, add an
+       * entry for the raw name (compliant proto3 JSON parsers must accept
+       * both). */
+      upb_strtable_insert(t, upb_fielddef_name(f), upb_value_constptr(f));
+    }
+
+    if (upb_fielddef_issubmsg(f)) {
+      add_jsonname_table(m, upb_fielddef_msgsubdef(f));
+    }
+  }
+
+  upb_gfree(buf);
+}
 
 /* Public API *****************************************************************/
 
-upb_json_parser *upb_json_parser_create(upb_env *env, upb_sink *output) {
+upb_json_parser *upb_json_parser_create(upb_env *env,
+                                        const upb_json_parsermethod *method,
+                                        upb_sink *output) {
 #ifndef NDEBUG
   const size_t size_before = upb_env_bytesallocated(env);
 #endif
@@ -11178,35 +12058,59 @@
   if (!p) return false;
 
   p->env = env;
+  p->method = method;
   p->limit = p->stack + UPB_JSON_MAX_DEPTH;
   p->accumulate_buf = NULL;
   p->accumulate_buf_size = 0;
-  upb_byteshandler_init(&p->input_handler_);
-  upb_byteshandler_setstring(&p->input_handler_, parse, NULL);
-  upb_byteshandler_setendstr(&p->input_handler_, end, NULL);
-  upb_bytessink_reset(&p->input_, &p->input_handler_, p);
+  upb_bytessink_reset(&p->input_, &method->input_handler_, p);
 
   json_parser_reset(p);
   upb_sink_reset(&p->top->sink, output->handlers, output->closure);
   p->top->m = upb_handlers_msgdef(output->handlers);
+  set_name_table(p, p->top);
 
   /* If this fails, uncomment and increase the value in parser.h. */
   /* fprintf(stderr, "%zd\n", upb_env_bytesallocated(env) - size_before); */
-  assert(upb_env_bytesallocated(env) - size_before <= UPB_JSON_PARSER_SIZE);
+  UPB_ASSERT_DEBUGVAR(upb_env_bytesallocated(env) - size_before <=
+                      UPB_JSON_PARSER_SIZE);
   return p;
 }
 
 upb_bytessink *upb_json_parser_input(upb_json_parser *p) {
   return &p->input_;
 }
+
+upb_json_parsermethod *upb_json_parsermethod_new(const upb_msgdef* md,
+                                                 const void* owner) {
+  static const struct upb_refcounted_vtbl vtbl = {visit_json_parsermethod,
+                                                  free_json_parsermethod};
+  upb_json_parsermethod *ret = upb_gmalloc(sizeof(*ret));
+  upb_refcounted_init(upb_json_parsermethod_upcast_mutable(ret), &vtbl, owner);
+
+  ret->msg = md;
+  upb_ref2(md, ret);
+
+  upb_byteshandler_init(&ret->input_handler_);
+  upb_byteshandler_setstring(&ret->input_handler_, parse, ret);
+  upb_byteshandler_setendstr(&ret->input_handler_, end, ret);
+
+  upb_inttable_init(&ret->name_tables, UPB_CTYPE_PTR);
+
+  add_jsonname_table(ret, md);
+
+  return ret;
+}
+
+const upb_byteshandler *upb_json_parsermethod_inputhandler(
+    const upb_json_parsermethod *m) {
+  return &m->input_handler_;
+}
 /*
 ** This currently uses snprintf() to format primitives, and could be optimized
 ** further.
 */
 
 
-#include <stdlib.h>
-#include <stdio.h>
 #include <string.h>
 #include <stdint.h>
 
@@ -11233,15 +12137,34 @@
 
 /* StringPiece; a pointer plus a length. */
 typedef struct {
-  const char *ptr;
+  char *ptr;
   size_t len;
 } strpc;
 
-strpc *newstrpc(upb_handlers *h, const upb_fielddef *f) {
-  strpc *ret = malloc(sizeof(*ret));
-  ret->ptr = upb_fielddef_name(f);
-  ret->len = strlen(ret->ptr);
-  upb_handlers_addcleanup(h, ret, free);
+void freestrpc(void *ptr) {
+  strpc *pc = ptr;
+  upb_gfree(pc->ptr);
+  upb_gfree(pc);
+}
+
+/* Convert fielddef name to JSON name and return as a string piece. */
+strpc *newstrpc(upb_handlers *h, const upb_fielddef *f,
+                bool preserve_fieldnames) {
+  /* TODO(haberman): handle malloc failure. */
+  strpc *ret = upb_gmalloc(sizeof(*ret));
+  if (preserve_fieldnames) {
+    ret->ptr = upb_gstrdup(upb_fielddef_name(f));
+    ret->len = strlen(ret->ptr);
+  } else {
+    size_t len;
+    ret->len = upb_fielddef_getjsonname(f, NULL, 0);
+    ret->ptr = upb_gmalloc(ret->len);
+    len = upb_fielddef_getjsonname(f, ret->ptr, ret->len);
+    UPB_ASSERT(len == ret->len);
+    ret->len--;  /* NULL */
+  }
+
+  upb_handlers_addcleanup(h, ret, freestrpc);
   return ret;
 }
 
@@ -11251,7 +12174,7 @@
     upb_json_printer *p, const char *buf, unsigned int len) {
   /* TODO: Will need to change if we support pushback from the sink. */
   size_t n = upb_bytessink_putbuf(p->output_, p->subc_, buf, len, NULL);
-  UPB_ASSERT_VAR(n, n == len);
+  UPB_ASSERT(n == len);
 }
 
 static void print_comma(upb_json_printer *p) {
@@ -11272,7 +12195,7 @@
   return uc < kControlCharLimit || uc == '"' || uc == '\\';
 }
 
-UPB_INLINE char* json_nice_escape(char c) {
+UPB_INLINE const char* json_nice_escape(char c) {
   switch (c) {
     case '"':  return "\\\"";
     case '\\': return "\\\\";
@@ -11601,7 +12524,7 @@
 
   while (remaining > 2) {
     /* TODO(haberman): handle encoded lengths > sizeof(data) */
-    UPB_ASSERT_VAR(limit, (limit - to) >= 4);
+    UPB_ASSERT((limit - to) >= 4);
 
     to[0] = base64[from[0] >> 2];
     to[1] = base64[((from[0] & 0x3) << 4) | (from[1] >> 4)];
@@ -11745,11 +12668,12 @@
 
 static void set_enum_hd(upb_handlers *h,
                         const upb_fielddef *f,
+                        bool preserve_fieldnames,
                         upb_handlerattr *attr) {
-  EnumHandlerData *hd = malloc(sizeof(EnumHandlerData));
+  EnumHandlerData *hd = upb_gmalloc(sizeof(EnumHandlerData));
   hd->enumdef = (const upb_enumdef *)upb_fielddef_subdef(f);
-  hd->keyname = newstrpc(h, f);
-  upb_handlers_addcleanup(h, hd, free);
+  hd->keyname = newstrpc(h, f, preserve_fieldnames);
+  upb_handlers_addcleanup(h, hd, upb_gfree);
   upb_handlerattr_sethandlerdata(attr, hd);
 }
 
@@ -11765,7 +12689,8 @@
  * our sources that emit mapentry messages do so canonically (with one key
  * field, and then one value field), so this is not a pressing concern at the
  * moment. */
-void printer_sethandlers_mapentry(const void *closure, upb_handlers *h) {
+void printer_sethandlers_mapentry(const void *closure, bool preserve_fieldnames,
+                                  upb_handlers *h) {
   const upb_msgdef *md = upb_handlers_msgdef(h);
 
   /* A mapentry message is printed simply as '"key": value'. Rather than
@@ -11803,7 +12728,7 @@
       upb_handlers_setstring(h, key_field, mapkey_bytes, &empty_attr);
       break;
     default:
-      assert(false);
+      UPB_ASSERT(false);
       break;
   }
 
@@ -11839,7 +12764,7 @@
       break;
     case UPB_TYPE_ENUM: {
       upb_handlerattr enum_attr = UPB_HANDLERATTR_INITIALIZER;
-      set_enum_hd(h, value_field, &enum_attr);
+      set_enum_hd(h, value_field, preserve_fieldnames, &enum_attr);
       upb_handlers_setint32(h, value_field, mapvalue_enum, &enum_attr);
       upb_handlerattr_uninit(&enum_attr);
       break;
@@ -11858,13 +12783,13 @@
   bool is_mapentry = upb_msgdef_mapentry(md);
   upb_handlerattr empty_attr = UPB_HANDLERATTR_INITIALIZER;
   upb_msg_field_iter i;
-
-  UPB_UNUSED(closure);
+  const bool *preserve_fieldnames_ptr = closure;
+  const bool preserve_fieldnames = *preserve_fieldnames_ptr;
 
   if (is_mapentry) {
     /* mapentry messages are sufficiently different that we handle them
      * separately. */
-    printer_sethandlers_mapentry(closure, h);
+    printer_sethandlers_mapentry(closure, preserve_fieldnames, h);
     return;
   }
 
@@ -11885,7 +12810,8 @@
     const upb_fielddef *f = upb_msg_iter_field(&i);
 
     upb_handlerattr name_attr = UPB_HANDLERATTR_INITIALIZER;
-    upb_handlerattr_sethandlerdata(&name_attr, newstrpc(h, f));
+    upb_handlerattr_sethandlerdata(&name_attr,
+                                   newstrpc(h, f, preserve_fieldnames));
 
     if (upb_fielddef_ismap(f)) {
       upb_handlers_setstartseq(h, f, startmap, &name_attr);
@@ -11908,7 +12834,7 @@
          * option later to control this behavior, but we will wait for a real
          * need first. */
         upb_handlerattr enum_attr = UPB_HANDLERATTR_INITIALIZER;
-        set_enum_hd(h, f, &enum_attr);
+        set_enum_hd(h, f, preserve_fieldnames, &enum_attr);
 
         if (upb_fielddef_isseq(f)) {
           upb_handlers_setint32(h, f, repeated_enum, &enum_attr);
@@ -11976,7 +12902,8 @@
   upb_sink_reset(&p->input_, h, p);
 
   /* If this fails, increase the value in printer.h. */
-  assert(upb_env_bytesallocated(e) - size_before <= UPB_JSON_PRINTER_SIZE);
+  UPB_ASSERT_DEBUGVAR(upb_env_bytesallocated(e) - size_before <=
+                      UPB_JSON_PRINTER_SIZE);
   return p;
 }
 
@@ -11985,6 +12912,8 @@
 }
 
 const upb_handlers *upb_json_printer_newhandlers(const upb_msgdef *md,
+                                                 bool preserve_fieldnames,
                                                  const void *owner) {
-  return upb_handlers_newfrozen(md, owner, printer_sethandlers, NULL);
+  return upb_handlers_newfrozen(
+      md, owner, printer_sethandlers, &preserve_fieldnames);
 }
diff --git a/php/ext/google/protobuf/upb.h b/php/ext/google/protobuf/upb.h
index 073faf4..c83b0e0 100644
--- a/php/ext/google/protobuf/upb.h
+++ b/php/ext/google/protobuf/upb.h
@@ -5,6 +5,7 @@
 **
 ** - upb::MessageDef (upb_msgdef): describes a "message" construct.
 ** - upb::FieldDef (upb_fielddef): describes a message field.
+** - upb::FileDef (upb_filedef): describes a .proto file and its defs.
 ** - upb::EnumDef (upb_enumdef): describes an enum.
 ** - upb::OneofDef (upb_oneofdef): describes a oneof.
 ** - upb::Def (upb_def): base class of all the others.
@@ -54,14 +55,19 @@
 ** store pointers or integers of at least 32 bits (upb isn't really useful on
 ** systems where sizeof(void*) < 4).
 **
-** The table must be homogeneous (all values of the same type).  In debug
+** The table must be homogenous (all values of the same type).  In debug
 ** mode, we check this on insert and lookup.
 */
 
 #ifndef UPB_TABLE_H_
 #define UPB_TABLE_H_
 
-#include <assert.h>
+// php.h intentionally defined NDEBUG. We have to define this macro in order to
+// be used together with php.h
+#ifndef NDEBUG
+#define NDEBUG
+#endif
+
 #include <stdint.h>
 #include <string.h>
 /*
@@ -79,6 +85,18 @@
 #include <stdbool.h>
 #include <stddef.h>
 
+#ifdef __cplusplus
+namespace upb {
+class Allocator;
+class Arena;
+class Environment;
+class ErrorSpace;
+class Status;
+template <int N> class InlinedArena;
+template <int N> class InlinedEnvironment;
+}
+#endif
+
 /* UPB_INLINE: inline if possible, emit standalone code if required. */
 #ifdef __cplusplus
 #define UPB_INLINE inline
@@ -146,6 +164,7 @@
 #define UPB_ASSERT_STDLAYOUT(type) \
   static_assert(std::is_standard_layout<type>::value, \
                 #type " must be standard layout");
+#define UPB_FINAL final
 #else  /* !defined(UPB_CXX11) */
 #define UPB_DISALLOW_COPY_AND_ASSIGN(class_name) \
   class_name(const class_name&); \
@@ -155,6 +174,7 @@
   ~class_name(); \
   UPB_DISALLOW_COPY_AND_ASSIGN(class_name)
 #define UPB_ASSERT_STDLAYOUT(type)
+#define UPB_FINAL
 #endif
 
 /* UPB_DECLARE_TYPE()
@@ -193,13 +213,15 @@
   template <>                                                     \
   class Pointer<cppname> : public PointerBase<cppname, cppbase> { \
    public:                                                        \
-    explicit Pointer(cppname* ptr) : PointerBase(ptr) {}          \
+    explicit Pointer(cppname* ptr)                                \
+        : PointerBase<cppname, cppbase>(ptr) {}                   \
   };                                                              \
   template <>                                                     \
   class Pointer<const cppname>                                    \
       : public PointerBase<const cppname, const cppbase> {        \
    public:                                                        \
-    explicit Pointer(const cppname* ptr) : PointerBase(ptr) {}    \
+    explicit Pointer(const cppname* ptr)                          \
+        : PointerBase<const cppname, const cppbase>(ptr) {}       \
   };                                                              \
   }
 
@@ -211,13 +233,15 @@
   template <>                                                                \
   class Pointer<cppname> : public PointerBase2<cppname, cppbase, cppbase2> { \
    public:                                                                   \
-    explicit Pointer(cppname* ptr) : PointerBase2(ptr) {}                    \
+    explicit Pointer(cppname* ptr)                                           \
+        : PointerBase2<cppname, cppbase, cppbase2>(ptr) {}                   \
   };                                                                         \
   template <>                                                                \
   class Pointer<const cppname>                                               \
       : public PointerBase2<const cppname, const cppbase, const cppbase2> {  \
    public:                                                                   \
-    explicit Pointer(const cppname* ptr) : PointerBase2(ptr) {}              \
+    explicit Pointer(const cppname* ptr)                                     \
+        : PointerBase2<const cppname, const cppbase, const cppbase2>(ptr) {} \
   };                                                                         \
   }
 
@@ -244,14 +268,22 @@
 
 #define UPB_UNUSED(var) (void)var
 
-/* For asserting something about a variable when the variable is not used for
- * anything else.  This prevents "unused variable" warnings when compiling in
- * debug mode. */
-#define UPB_ASSERT_VAR(var, predicate) UPB_UNUSED(var); assert(predicate)
+/* UPB_ASSERT(): in release mode, we use the expression without letting it be
+ * evaluated.  This prevents "unused variable" warnings. */
+#ifdef NDEBUG
+#define UPB_ASSERT(expr) do {} while (false && (expr))
+#else
+#define UPB_ASSERT(expr) assert(expr)
+#endif
+
+/* UPB_ASSERT_DEBUGVAR(): assert that uses functions or variables that only
+ * exist in debug mode.  This turns into regular assert. */
+#define UPB_ASSERT_DEBUGVAR(expr) assert(expr)
 
 /* Generic function type. */
 typedef void upb_func();
 
+
 /* C++ Casts ******************************************************************/
 
 #ifdef __cplusplus
@@ -329,129 +361,18 @@
 #endif
 
 
-/* upb::reffed_ptr ************************************************************/
+/* upb::ErrorSpace ************************************************************/
 
-#ifdef __cplusplus
-
-#include <algorithm>  /* For std::swap(). */
-
-namespace upb {
-
-/* Provides RAII semantics for upb refcounted objects.  Each reffed_ptr owns a
- * ref on whatever object it points to (if any). */
-template <class T> class reffed_ptr {
- public:
-  reffed_ptr() : ptr_(NULL) {}
-
-  /* If ref_donor is NULL, takes a new ref, otherwise adopts from ref_donor. */
-  template <class U>
-  reffed_ptr(U* val, const void* ref_donor = NULL)
-      : ptr_(upb::upcast(val)) {
-    if (ref_donor) {
-      assert(ptr_);
-      ptr_->DonateRef(ref_donor, this);
-    } else if (ptr_) {
-      ptr_->Ref(this);
-    }
-  }
-
-  template <class U>
-  reffed_ptr(const reffed_ptr<U>& other)
-      : ptr_(upb::upcast(other.get())) {
-    if (ptr_) ptr_->Ref(this);
-  }
-
-  ~reffed_ptr() { if (ptr_) ptr_->Unref(this); }
-
-  template <class U>
-  reffed_ptr& operator=(const reffed_ptr<U>& other) {
-    reset(other.get());
-    return *this;
-  }
-
-  reffed_ptr& operator=(const reffed_ptr& other) {
-    reset(other.get());
-    return *this;
-  }
-
-  /* TODO(haberman): add C++11 move construction/assignment for greater
-   * efficiency. */
-
-  void swap(reffed_ptr& other) {
-    if (ptr_ == other.ptr_) {
-      return;
-    }
-
-    if (ptr_) ptr_->DonateRef(this, &other);
-    if (other.ptr_) other.ptr_->DonateRef(&other, this);
-    std::swap(ptr_, other.ptr_);
-  }
-
-  T& operator*() const {
-    assert(ptr_);
-    return *ptr_;
-  }
-
-  T* operator->() const {
-    assert(ptr_);
-    return ptr_;
-  }
-
-  T* get() const { return ptr_; }
-
-  /* If ref_donor is NULL, takes a new ref, otherwise adopts from ref_donor. */
-  template <class U>
-  void reset(U* ptr = NULL, const void* ref_donor = NULL) {
-    reffed_ptr(ptr, ref_donor).swap(*this);
-  }
-
-  template <class U>
-  reffed_ptr<U> down_cast() {
-    return reffed_ptr<U>(upb::down_cast<U*>(get()));
-  }
-
-  template <class U>
-  reffed_ptr<U> dyn_cast() {
-    return reffed_ptr<U>(upb::dyn_cast<U*>(get()));
-  }
-
-  /* Plain release() is unsafe; if we were the only owner, it would leak the
-   * object.  Instead we provide this: */
-  T* ReleaseTo(const void* new_owner) {
-    T* ret = NULL;
-    ptr_->DonateRef(this, new_owner);
-    std::swap(ret, ptr_);
-    return ret;
-  }
-
- private:
-  T* ptr_;
-};
-
-}  /* namespace upb */
-
-#endif  /* __cplusplus */
-
-
-/* upb::Status ****************************************************************/
-
-#ifdef __cplusplus
-namespace upb {
-class ErrorSpace;
-class Status;
-}
-#endif
+/* A upb::ErrorSpace represents some domain of possible error values.  This lets
+ * upb::Status attach specific error codes to operations, like POSIX/C errno,
+ * Win32 error codes, etc.  Clients who want to know the very specific error
+ * code can check the error space and then know the type of the integer code.
+ *
+ * NOTE: upb::ErrorSpace is currently not used and should be considered
+ * experimental.  It is important primarily in cases where upb is performing
+ * I/O, but upb doesn't currently have any components that do this. */
 
 UPB_DECLARE_TYPE(upb::ErrorSpace, upb_errorspace)
-UPB_DECLARE_TYPE(upb::Status, upb_status)
-
-/* The maximum length of an error message before it will get truncated. */
-#define UPB_STATUS_MAX_MESSAGE 128
-
-/* An error callback function is used to report errors from some component.
- * The function can return "true" to indicate that the component should try
- * to recover and proceed, but this is not always possible. */
-typedef bool upb_errcb_t(void *closure, const upb_status* status);
 
 #ifdef __cplusplus
 class upb::ErrorSpace {
@@ -459,67 +380,21 @@
 struct upb_errorspace {
 #endif
   const char *name;
-  /* Should the error message in the status object according to this code. */
-  void (*set_message)(upb_status* status, int code);
 };
 
-#ifdef __cplusplus
 
-/* Object representing a success or failure status.
+/* upb::Status ****************************************************************/
+
+/* upb::Status represents a success or failure status and error message.
  * It owns no resources and allocates no memory, so it should work
  * even in OOM situations. */
+UPB_DECLARE_TYPE(upb::Status, upb_status)
 
-class upb::Status {
- public:
-  Status();
+/* The maximum length of an error message before it will get truncated. */
+#define UPB_STATUS_MAX_MESSAGE 128
 
-  /* Returns true if there is no error. */
-  bool ok() const;
+UPB_BEGIN_EXTERN_C
 
-  /* Optional error space and code, useful if the caller wants to
-   * programmatically check the specific kind of error. */
-  ErrorSpace* error_space();
-  int code() const;
-
-  const char *error_message() const;
-
-  /* The error message will be truncated if it is longer than
-   * UPB_STATUS_MAX_MESSAGE-4. */
-  void SetErrorMessage(const char* msg);
-  void SetFormattedErrorMessage(const char* fmt, ...);
-
-  /* If there is no error message already, this will use the ErrorSpace to
-   * populate the error message for this code.  The caller can still call
-   * SetErrorMessage() to give a more specific message. */
-  void SetErrorCode(ErrorSpace* space, int code);
-
-  /* Resets the status to a successful state with no message. */
-  void Clear();
-
-  void CopyFrom(const Status& other);
-
- private:
-  UPB_DISALLOW_COPY_AND_ASSIGN(Status)
-#else
-struct upb_status {
-#endif
-  bool ok_;
-
-  /* Specific status code defined by some error space (optional). */
-  int code_;
-  upb_errorspace *error_space_;
-
-  /* Error message; NULL-terminated. */
-  char msg[UPB_STATUS_MAX_MESSAGE];
-};
-
-#define UPB_STATUS_INIT {true, 0, NULL, {0}}
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* The returned string is invalidated by any other call into the status. */
 const char *upb_status_errmsg(const upb_status *status);
 bool upb_ok(const upb_status *status);
 upb_errorspace *upb_status_errspace(const upb_status *status);
@@ -532,40 +407,384 @@
 void upb_status_seterrmsg(upb_status *status, const char *msg);
 void upb_status_seterrf(upb_status *status, const char *fmt, ...);
 void upb_status_vseterrf(upb_status *status, const char *fmt, va_list args);
-void upb_status_seterrcode(upb_status *status, upb_errorspace *space, int code);
 void upb_status_copy(upb_status *to, const upb_status *from);
 
+UPB_END_EXTERN_C
+
 #ifdef __cplusplus
-}  /* extern "C" */
 
-namespace upb {
+class upb::Status {
+ public:
+  Status() { upb_status_clear(this); }
 
-/* C++ Wrappers */
-inline Status::Status() { Clear(); }
-inline bool Status::ok() const { return upb_ok(this); }
-inline const char* Status::error_message() const {
-  return upb_status_errmsg(this);
-}
-inline void Status::SetErrorMessage(const char* msg) {
-  upb_status_seterrmsg(this, msg);
-}
-inline void Status::SetFormattedErrorMessage(const char* fmt, ...) {
-  va_list args;
-  va_start(args, fmt);
-  upb_status_vseterrf(this, fmt, args);
-  va_end(args);
-}
-inline void Status::SetErrorCode(ErrorSpace* space, int code) {
-  upb_status_seterrcode(this, space, code);
-}
-inline void Status::Clear() { upb_status_clear(this); }
-inline void Status::CopyFrom(const Status& other) {
-  upb_status_copy(this, &other);
-}
+  /* Returns true if there is no error. */
+  bool ok() const { return upb_ok(this); }
 
-}  /* namespace upb */
+  /* Optional error space and code, useful if the caller wants to
+   * programmatically check the specific kind of error. */
+  ErrorSpace* error_space() { return upb_status_errspace(this); }
+  int error_code() const { return upb_status_errcode(this); }
 
+  /* The returned string is invalidated by any other call into the status. */
+  const char *error_message() const { return upb_status_errmsg(this); }
+
+  /* The error message will be truncated if it is longer than
+   * UPB_STATUS_MAX_MESSAGE-4. */
+  void SetErrorMessage(const char* msg) { upb_status_seterrmsg(this, msg); }
+  void SetFormattedErrorMessage(const char* fmt, ...) {
+    va_list args;
+    va_start(args, fmt);
+    upb_status_vseterrf(this, fmt, args);
+    va_end(args);
+  }
+
+  /* Resets the status to a successful state with no message. */
+  void Clear() { upb_status_clear(this); }
+
+  void CopyFrom(const Status& other) { upb_status_copy(this, &other); }
+
+ private:
+  UPB_DISALLOW_COPY_AND_ASSIGN(Status)
+#else
+struct upb_status {
 #endif
+  bool ok_;
+
+  /* Specific status code defined by some error space (optional). */
+  int code_;
+  upb_errorspace *error_space_;
+
+  /* TODO(haberman): add file/line of error? */
+
+  /* Error message; NULL-terminated. */
+  char msg[UPB_STATUS_MAX_MESSAGE];
+};
+
+#define UPB_STATUS_INIT {true, 0, NULL, {0}}
+
+
+/** Built-in error spaces. ****************************************************/
+
+/* Errors raised by upb that we want to be able to detect programmatically. */
+typedef enum {
+  UPB_NOMEM   /* Can't reuse ENOMEM because it is POSIX, not ISO C. */
+} upb_errcode_t;
+
+extern upb_errorspace upb_upberr;
+
+void upb_upberr_setoom(upb_status *s);
+
+/* Since errno is defined by standard C, we define an error space for it in
+ * core upb.  Other error spaces should be defined in other, platform-specific
+ * modules. */
+
+extern upb_errorspace upb_errnoerr;
+
+
+/** upb::Allocator ************************************************************/
+
+/* A upb::Allocator is a possibly-stateful allocator object.
+ *
+ * It could either be an arena allocator (which doesn't require individual
+ * free() calls) or a regular malloc() (which does).  The client must therefore
+ * free memory unless it knows that the allocator is an arena allocator. */
+UPB_DECLARE_TYPE(upb::Allocator, upb_alloc)
+
+/* A malloc()/free() function.
+ * If "size" is 0 then the function acts like free(), otherwise it acts like
+ * realloc().  Only "oldsize" bytes from a previous allocation are preserved. */
+typedef void *upb_alloc_func(upb_alloc *alloc, void *ptr, size_t oldsize,
+                             size_t size);
+
+#ifdef __cplusplus
+
+class upb::Allocator UPB_FINAL {
+ public:
+  Allocator() {}
+
+ private:
+  UPB_DISALLOW_COPY_AND_ASSIGN(Allocator)
+
+ public:
+#else
+struct upb_alloc {
+#endif  /* __cplusplus */
+  upb_alloc_func *func;
+};
+
+UPB_INLINE void *upb_malloc(upb_alloc *alloc, size_t size) {
+  UPB_ASSERT(size > 0);
+  return alloc->func(alloc, NULL, 0, size);
+}
+
+UPB_INLINE void *upb_realloc(upb_alloc *alloc, void *ptr, size_t oldsize,
+                             size_t size) {
+  UPB_ASSERT(size > 0);
+  return alloc->func(alloc, ptr, oldsize, size);
+}
+
+UPB_INLINE void upb_free(upb_alloc *alloc, void *ptr) {
+  alloc->func(alloc, ptr, 0, 0);
+}
+
+/* The global allocator used by upb.  Uses the standard malloc()/free(). */
+
+extern upb_alloc upb_alloc_global;
+
+/* Functions that hard-code the global malloc.
+ *
+ * We still get benefit because we can put custom logic into our global
+ * allocator, like injecting out-of-memory faults in debug/testing builds. */
+
+UPB_INLINE void *upb_gmalloc(size_t size) {
+  return upb_malloc(&upb_alloc_global, size);
+}
+
+UPB_INLINE void *upb_grealloc(void *ptr, size_t oldsize, size_t size) {
+  return upb_realloc(&upb_alloc_global, ptr, oldsize, size);
+}
+
+UPB_INLINE void upb_gfree(void *ptr) {
+  upb_free(&upb_alloc_global, ptr);
+}
+
+/* upb::Arena *****************************************************************/
+
+/* upb::Arena is a specific allocator implementation that uses arena allocation.
+ * The user provides an allocator that will be used to allocate the underlying
+ * arena blocks.  Arenas by nature do not require the individual allocations
+ * to be freed.  However the Arena does allow users to register cleanup
+ * functions that will run when the arena is destroyed.
+ *
+ * A upb::Arena is *not* thread-safe.
+ *
+ * You could write a thread-safe arena allocator that satisfies the
+ * upb::Allocator interface, but it would not be as efficient for the
+ * single-threaded case. */
+UPB_DECLARE_TYPE(upb::Arena, upb_arena)
+
+typedef void upb_cleanup_func(void *ud);
+
+#define UPB_ARENA_BLOCK_OVERHEAD (sizeof(size_t)*4)
+
+UPB_BEGIN_EXTERN_C
+
+void upb_arena_init(upb_arena *a);
+void upb_arena_init2(upb_arena *a, void *mem, size_t n, upb_alloc *alloc);
+void upb_arena_uninit(upb_arena *a);
+upb_alloc *upb_arena_alloc(upb_arena *a);
+bool upb_arena_addcleanup(upb_arena *a, upb_cleanup_func *func, void *ud);
+size_t upb_arena_bytesallocated(const upb_arena *a);
+void upb_arena_setnextblocksize(upb_arena *a, size_t size);
+void upb_arena_setmaxblocksize(upb_arena *a, size_t size);
+
+UPB_END_EXTERN_C
+
+#ifdef __cplusplus
+
+class upb::Arena {
+ public:
+  /* A simple arena with no initial memory block and the default allocator. */
+  Arena() { upb_arena_init(this); }
+
+  /* Constructs an arena with the given initial block which allocates blocks
+   * with the given allocator.  The given allocator must outlive the Arena.
+   *
+   * If you pass NULL for the allocator it will default to the global allocator
+   * upb_alloc_global, and NULL/0 for the initial block will cause there to be
+   * no initial block. */
+  Arena(void *mem, size_t len, Allocator* a) {
+    upb_arena_init2(this, mem, len, a);
+  }
+
+  ~Arena() { upb_arena_uninit(this); }
+
+  /* Sets the size of the next block the Arena will request (unless the
+   * requested allocation is larger).  Each block will double in size until the
+   * max limit is reached. */
+  void SetNextBlockSize(size_t size) { upb_arena_setnextblocksize(this, size); }
+
+  /* Sets the maximum block size.  No blocks larger than this will be requested
+   * from the underlying allocator unless individual arena allocations are
+   * larger. */
+  void SetMaxBlockSize(size_t size) { upb_arena_setmaxblocksize(this, size); }
+
+  /* Allows this arena to be used as a generic allocator.
+   *
+   * The arena does not need free() calls so when using Arena as an allocator
+   * it is safe to skip them.  However they are no-ops so there is no harm in
+   * calling free() either. */
+  Allocator* allocator() { return upb_arena_alloc(this); }
+
+  /* Add a cleanup function to run when the arena is destroyed.
+   * Returns false on out-of-memory. */
+  bool AddCleanup(upb_cleanup_func* func, void* ud) {
+    return upb_arena_addcleanup(this, func, ud);
+  }
+
+  /* Total number of bytes that have been allocated.  It is undefined what
+   * Realloc() does to this counter. */
+  size_t BytesAllocated() const {
+    return upb_arena_bytesallocated(this);
+  }
+
+ private:
+  UPB_DISALLOW_COPY_AND_ASSIGN(Arena)
+
+#else
+struct upb_arena {
+#endif  /* __cplusplus */
+  /* We implement the allocator interface.
+   * This must be the first member of upb_arena! */
+  upb_alloc alloc;
+
+  /* Allocator to allocate arena blocks.  We are responsible for freeing these
+   * when we are destroyed. */
+  upb_alloc *block_alloc;
+
+  size_t bytes_allocated;
+  size_t next_block_size;
+  size_t max_block_size;
+
+  /* Linked list of blocks.  Points to an arena_block, defined in env.c */
+  void *block_head;
+
+  /* Cleanup entries.  Pointer to a cleanup_ent, defined in env.c */
+  void *cleanup_head;
+
+  /* For future expansion, since the size of this struct is exposed to users. */
+  void *future1;
+  void *future2;
+};
+
+
+/* upb::Environment ***********************************************************/
+
+/* A upb::Environment provides a means for injecting malloc and an
+ * error-reporting callback into encoders/decoders.  This allows them to be
+ * independent of nearly all assumptions about their actual environment.
+ *
+ * It is also a container for allocating the encoders/decoders themselves that
+ * insulates clients from knowing their actual size.  This provides ABI
+ * compatibility even if the size of the objects change.  And this allows the
+ * structure definitions to be in the .c files instead of the .h files, making
+ * the .h files smaller and more readable.
+ *
+ * We might want to consider renaming this to "Pipeline" if/when the concept of
+ * a pipeline element becomes more formalized. */
+UPB_DECLARE_TYPE(upb::Environment, upb_env)
+
+/* A function that receives an error report from an encoder or decoder.  The
+ * callback can return true to request that the error should be recovered, but
+ * if the error is not recoverable this has no effect. */
+typedef bool upb_error_func(void *ud, const upb_status *status);
+
+UPB_BEGIN_EXTERN_C
+
+void upb_env_init(upb_env *e);
+void upb_env_init2(upb_env *e, void *mem, size_t n, upb_alloc *alloc);
+void upb_env_uninit(upb_env *e);
+
+void upb_env_initonly(upb_env *e);
+
+upb_arena *upb_env_arena(upb_env *e);
+bool upb_env_ok(const upb_env *e);
+void upb_env_seterrorfunc(upb_env *e, upb_error_func *func, void *ud);
+
+/* Convenience wrappers around the methods of the contained arena. */
+void upb_env_reporterrorsto(upb_env *e, upb_status *s);
+bool upb_env_reporterror(upb_env *e, const upb_status *s);
+void *upb_env_malloc(upb_env *e, size_t size);
+void *upb_env_realloc(upb_env *e, void *ptr, size_t oldsize, size_t size);
+void upb_env_free(upb_env *e, void *ptr);
+bool upb_env_addcleanup(upb_env *e, upb_cleanup_func *func, void *ud);
+size_t upb_env_bytesallocated(const upb_env *e);
+
+UPB_END_EXTERN_C
+
+#ifdef __cplusplus
+
+class upb::Environment {
+ public:
+  /* The given Arena must outlive this environment. */
+  Environment() { upb_env_initonly(this); }
+
+  Environment(void *mem, size_t len, Allocator *a) : arena_(mem, len, a) {
+    upb_env_initonly(this);
+  }
+
+  Arena* arena() { return upb_env_arena(this); }
+
+  /* Set a custom error reporting function. */
+  void SetErrorFunction(upb_error_func* func, void* ud) {
+    upb_env_seterrorfunc(this, func, ud);
+  }
+
+  /* Set the error reporting function to simply copy the status to the given
+   * status and abort. */
+  void ReportErrorsTo(Status* status) { upb_env_reporterrorsto(this, status); }
+
+  /* Returns true if all allocations and AddCleanup() calls have succeeded,
+   * and no errors were reported with ReportError() (except ones that recovered
+   * successfully). */
+  bool ok() const { return upb_env_ok(this); }
+
+  /* Reports an error to this environment's callback, returning true if
+   * the caller should try to recover. */
+  bool ReportError(const Status* status) {
+    return upb_env_reporterror(this, status);
+  }
+
+ private:
+  UPB_DISALLOW_COPY_AND_ASSIGN(Environment)
+
+#else
+struct upb_env {
+#endif  /* __cplusplus */
+  upb_arena arena_;
+  upb_error_func *error_func_;
+  void *error_ud_;
+  bool ok_;
+};
+
+
+/* upb::InlinedArena **********************************************************/
+/* upb::InlinedEnvironment ****************************************************/
+
+/* upb::InlinedArena and upb::InlinedEnvironment seed their arenas with a
+ * predefined amount of memory.  No heap memory will be allocated until the
+ * initial block is exceeded.
+ *
+ * These types only exist in C++ */
+
+#ifdef __cplusplus
+
+template <int N> class upb::InlinedArena : public upb::Arena {
+ public:
+  InlinedArena() : Arena(initial_block_, N, NULL) {}
+  explicit InlinedArena(Allocator* a) : Arena(initial_block_, N, a) {}
+
+ private:
+  UPB_DISALLOW_COPY_AND_ASSIGN(InlinedArena)
+
+  char initial_block_[N + UPB_ARENA_BLOCK_OVERHEAD];
+};
+
+template <int N> class upb::InlinedEnvironment : public upb::Environment {
+ public:
+  InlinedEnvironment() : Environment(initial_block_, N, NULL) {}
+  explicit InlinedEnvironment(Allocator *a)
+      : Environment(initial_block_, N, a) {}
+
+ private:
+  UPB_DISALLOW_COPY_AND_ASSIGN(InlinedEnvironment)
+
+  char initial_block_[N + UPB_ARENA_BLOCK_OVERHEAD];
+};
+
+#endif  /* __cplusplus */
+
+
 
 #endif  /* UPB_H_ */
 
@@ -607,10 +826,14 @@
 #endif
 
 /* Like strdup(), which isn't always available since it's not ANSI C. */
-char *upb_strdup(const char *s);
+char *upb_strdup(const char *s, upb_alloc *a);
 /* Variant that works with a length-delimited rather than NULL-delimited string,
  * as supported by strtable. */
-char *upb_strdup2(const char *s, size_t len);
+char *upb_strdup2(const char *s, size_t len, upb_alloc *a);
+
+UPB_INLINE char *upb_gstrdup(const char *s) {
+  return upb_strdup(s, &upb_alloc_global);
+}
 
 UPB_INLINE void _upb_value_setval(upb_value *v, uint64_t val,
                                   upb_ctype_t ctype) {
@@ -643,7 +866,7 @@
     return ret; \
   } \
   UPB_INLINE type_t upb_value_get ## name(upb_value val) { \
-    assert(val.ctype == proto_type); \
+    UPB_ASSERT_DEBUGVAR(val.ctype == proto_type); \
     return (type_t)(converter)val.val; \
   }
 
@@ -787,14 +1010,40 @@
    * initialize const hash tables.  Then we cast away const when we have to.
    */
   const upb_tabent *entries;
+
+#ifndef NDEBUG
+  /* This table's allocator.  We make the user pass it in to every relevant
+   * function and only use this to check it in debug mode.  We do this solely
+   * to keep upb_table as small as possible.  This might seem slightly paranoid
+   * but the plan is to use upb_table for all map fields and extension sets in
+   * a forthcoming message representation, so there could be a lot of these.
+   * If this turns out to be too annoying later, we can change it (since this
+   * is an internal-only header file). */
+  upb_alloc *alloc;
+#endif
 } upb_table;
 
+#ifdef NDEBUG
+#  define UPB_TABLE_INIT(count, mask, ctype, size_lg2, entries) \
+     {count, mask, ctype, size_lg2, entries}
+#else
+#  ifdef UPB_DEBUG_REFS
+/* At the moment the only mutable tables we statically initialize are debug
+ * ref tables. */
+#    define UPB_TABLE_INIT(count, mask, ctype, size_lg2, entries) \
+       {count, mask, ctype, size_lg2, entries, &upb_alloc_debugrefs}
+#  else
+#    define UPB_TABLE_INIT(count, mask, ctype, size_lg2, entries) \
+       {count, mask, ctype, size_lg2, entries, NULL}
+#  endif
+#endif
+
 typedef struct {
   upb_table t;
 } upb_strtable;
 
 #define UPB_STRTABLE_INIT(count, mask, ctype, size_lg2, entries) \
-  {{count, mask, ctype, size_lg2, entries}}
+  {UPB_TABLE_INIT(count, mask, ctype, size_lg2, entries)}
 
 #define UPB_EMPTY_STRTABLE_INIT(ctype)                           \
   UPB_STRTABLE_INIT(0, 0, ctype, 0, NULL)
@@ -807,7 +1056,7 @@
 } upb_inttable;
 
 #define UPB_INTTABLE_INIT(count, mask, ctype, size_lg2, ent, a, asize, acount) \
-  {{count, mask, ctype, size_lg2, ent}, a, asize, acount}
+  {UPB_TABLE_INIT(count, mask, ctype, size_lg2, ent), a, asize, acount}
 
 #define UPB_EMPTY_INTTABLE_INIT(ctype) \
   UPB_INTTABLE_INIT(0, 0, ctype, 0, NULL, NULL, 0, 0)
@@ -847,10 +1096,26 @@
 
 /* Initialize and uninitialize a table, respectively.  If memory allocation
  * failed, false is returned that the table is uninitialized. */
-bool upb_inttable_init(upb_inttable *table, upb_ctype_t ctype);
-bool upb_strtable_init(upb_strtable *table, upb_ctype_t ctype);
-void upb_inttable_uninit(upb_inttable *table);
-void upb_strtable_uninit(upb_strtable *table);
+bool upb_inttable_init2(upb_inttable *table, upb_ctype_t ctype, upb_alloc *a);
+bool upb_strtable_init2(upb_strtable *table, upb_ctype_t ctype, upb_alloc *a);
+void upb_inttable_uninit2(upb_inttable *table, upb_alloc *a);
+void upb_strtable_uninit2(upb_strtable *table, upb_alloc *a);
+
+UPB_INLINE bool upb_inttable_init(upb_inttable *table, upb_ctype_t ctype) {
+  return upb_inttable_init2(table, ctype, &upb_alloc_global);
+}
+
+UPB_INLINE bool upb_strtable_init(upb_strtable *table, upb_ctype_t ctype) {
+  return upb_strtable_init2(table, ctype, &upb_alloc_global);
+}
+
+UPB_INLINE void upb_inttable_uninit(upb_inttable *table) {
+  upb_inttable_uninit2(table, &upb_alloc_global);
+}
+
+UPB_INLINE void upb_strtable_uninit(upb_strtable *table) {
+  upb_strtable_uninit2(table, &upb_alloc_global);
+}
 
 /* Returns the number of values in the table. */
 size_t upb_inttable_count(const upb_inttable *t);
@@ -865,9 +1130,20 @@
  *
  * If a table resize was required but memory allocation failed, false is
  * returned and the table is unchanged. */
-bool upb_inttable_insert(upb_inttable *t, uintptr_t key, upb_value val);
-bool upb_strtable_insert2(upb_strtable *t, const char *key, size_t len,
-                          upb_value val);
+bool upb_inttable_insert2(upb_inttable *t, uintptr_t key, upb_value val,
+                          upb_alloc *a);
+bool upb_strtable_insert3(upb_strtable *t, const char *key, size_t len,
+                          upb_value val, upb_alloc *a);
+
+UPB_INLINE bool upb_inttable_insert(upb_inttable *t, uintptr_t key,
+                                    upb_value val) {
+  return upb_inttable_insert2(t, key, val, &upb_alloc_global);
+}
+
+UPB_INLINE bool upb_strtable_insert2(upb_strtable *t, const char *key,
+                                     size_t len, upb_value val) {
+  return upb_strtable_insert3(t, key, len, val, &upb_alloc_global);
+}
 
 /* For NULL-terminated strings. */
 UPB_INLINE bool upb_strtable_insert(upb_strtable *t, const char *key,
@@ -890,8 +1166,13 @@
 /* Removes an item from the table.  Returns true if the remove was successful,
  * and stores the removed item in *val if non-NULL. */
 bool upb_inttable_remove(upb_inttable *t, uintptr_t key, upb_value *val);
-bool upb_strtable_remove2(upb_strtable *t, const char *key, size_t len,
-                          upb_value *val);
+bool upb_strtable_remove3(upb_strtable *t, const char *key, size_t len,
+                          upb_value *val, upb_alloc *alloc);
+
+UPB_INLINE bool upb_strtable_remove2(upb_strtable *t, const char *key,
+                                     size_t len, upb_value *val) {
+  return upb_strtable_remove3(t, key, len, val, &upb_alloc_global);
+}
 
 /* For NULL-terminated strings. */
 UPB_INLINE bool upb_strtable_remove(upb_strtable *t, const char *key,
@@ -906,19 +1187,33 @@
 
 /* Handy routines for treating an inttable like a stack.  May not be mixed with
  * other insert/remove calls. */
-bool upb_inttable_push(upb_inttable *t, upb_value val);
+bool upb_inttable_push2(upb_inttable *t, upb_value val, upb_alloc *a);
 upb_value upb_inttable_pop(upb_inttable *t);
 
+UPB_INLINE bool upb_inttable_push(upb_inttable *t, upb_value val) {
+  return upb_inttable_push2(t, val, &upb_alloc_global);
+}
+
 /* Convenience routines for inttables with pointer keys. */
-bool upb_inttable_insertptr(upb_inttable *t, const void *key, upb_value val);
+bool upb_inttable_insertptr2(upb_inttable *t, const void *key, upb_value val,
+                             upb_alloc *a);
 bool upb_inttable_removeptr(upb_inttable *t, const void *key, upb_value *val);
 bool upb_inttable_lookupptr(
     const upb_inttable *t, const void *key, upb_value *val);
 
+UPB_INLINE bool upb_inttable_insertptr(upb_inttable *t, const void *key,
+                                       upb_value val) {
+  return upb_inttable_insertptr2(t, key, val, &upb_alloc_global);
+}
+
 /* Optimizes the table for the current set of entries, for both memory use and
  * lookup time.  Client should call this after all entries have been inserted;
  * inserting more entries is legal, but will likely require a table resize. */
-void upb_inttable_compact(upb_inttable *t);
+void upb_inttable_compact2(upb_inttable *t, upb_alloc *a);
+
+UPB_INLINE void upb_inttable_compact(upb_inttable *t) {
+  upb_inttable_compact2(t, &upb_alloc_global);
+}
 
 /* A special-case inlinable version of the lookup routine for 32-bit
  * integers. */
@@ -947,7 +1242,7 @@
 }
 
 /* Exposed for testing only. */
-bool upb_strtable_resize(upb_strtable *t, size_t size_lg2);
+bool upb_strtable_resize(upb_strtable *t, size_t size_lg2, upb_alloc *a);
 
 /* Iterators ******************************************************************/
 
@@ -992,8 +1287,8 @@
 void upb_strtable_begin(upb_strtable_iter *i, const upb_strtable *t);
 void upb_strtable_next(upb_strtable_iter *i);
 bool upb_strtable_done(const upb_strtable_iter *i);
-const char *upb_strtable_iter_key(upb_strtable_iter *i);
-size_t upb_strtable_iter_keylength(upb_strtable_iter *i);
+const char *upb_strtable_iter_key(const upb_strtable_iter *i);
+size_t upb_strtable_iter_keylength(const upb_strtable_iter *i);
 upb_value upb_strtable_iter_value(const upb_strtable_iter *i);
 void upb_strtable_iter_setdone(upb_strtable_iter *i);
 bool upb_strtable_iter_isequal(const upb_strtable_iter *i1,
@@ -1046,7 +1341,10 @@
 /* #define UPB_DEBUG_REFS */
 
 #ifdef __cplusplus
-namespace upb { class RefCounted; }
+namespace upb {
+class RefCounted;
+template <class T> class reffed_ptr;
+}
 #endif
 
 UPB_DECLARE_TYPE(upb::RefCounted, upb_refcounted)
@@ -1114,10 +1412,12 @@
 };
 
 #ifdef UPB_DEBUG_REFS
-#define UPB_REFCOUNT_INIT(refs, ref2s) \
-    {&static_refcount, NULL, NULL, 0, true, refs, ref2s}
+extern upb_alloc upb_alloc_debugrefs;
+#define UPB_REFCOUNT_INIT(vtbl, refs, ref2s) \
+    {&static_refcount, NULL, vtbl, 0, true, refs, ref2s}
 #else
-#define UPB_REFCOUNT_INIT(refs, ref2s) {&static_refcount, NULL, NULL, 0, true}
+#define UPB_REFCOUNT_INIT(vtbl, refs, ref2s) \
+    {&static_refcount, NULL, vtbl, 0, true}
 #endif
 
 UPB_BEGIN_EXTERN_C
@@ -1250,6 +1550,111 @@
 }  /* namespace upb */
 #endif
 
+
+/* upb::reffed_ptr ************************************************************/
+
+#ifdef __cplusplus
+
+#include <algorithm>  /* For std::swap(). */
+
+/* Provides RAII semantics for upb refcounted objects.  Each reffed_ptr owns a
+ * ref on whatever object it points to (if any). */
+template <class T> class upb::reffed_ptr {
+ public:
+  reffed_ptr() : ptr_(NULL) {}
+
+  /* If ref_donor is NULL, takes a new ref, otherwise adopts from ref_donor. */
+  template <class U>
+  reffed_ptr(U* val, const void* ref_donor = NULL)
+      : ptr_(upb::upcast(val)) {
+    if (ref_donor) {
+      UPB_ASSERT(ptr_);
+      ptr_->DonateRef(ref_donor, this);
+    } else if (ptr_) {
+      ptr_->Ref(this);
+    }
+  }
+
+  template <class U>
+  reffed_ptr(const reffed_ptr<U>& other)
+      : ptr_(upb::upcast(other.get())) {
+    if (ptr_) ptr_->Ref(this);
+  }
+
+  reffed_ptr(const reffed_ptr& other)
+      : ptr_(upb::upcast(other.get())) {
+    if (ptr_) ptr_->Ref(this);
+  }
+
+  ~reffed_ptr() { if (ptr_) ptr_->Unref(this); }
+
+  template <class U>
+  reffed_ptr& operator=(const reffed_ptr<U>& other) {
+    reset(other.get());
+    return *this;
+  }
+
+  reffed_ptr& operator=(const reffed_ptr& other) {
+    reset(other.get());
+    return *this;
+  }
+
+  /* TODO(haberman): add C++11 move construction/assignment for greater
+   * efficiency. */
+
+  void swap(reffed_ptr& other) {
+    if (ptr_ == other.ptr_) {
+      return;
+    }
+
+    if (ptr_) ptr_->DonateRef(this, &other);
+    if (other.ptr_) other.ptr_->DonateRef(&other, this);
+    std::swap(ptr_, other.ptr_);
+  }
+
+  T& operator*() const {
+    UPB_ASSERT(ptr_);
+    return *ptr_;
+  }
+
+  T* operator->() const {
+    UPB_ASSERT(ptr_);
+    return ptr_;
+  }
+
+  T* get() const { return ptr_; }
+
+  /* If ref_donor is NULL, takes a new ref, otherwise adopts from ref_donor. */
+  template <class U>
+  void reset(U* ptr = NULL, const void* ref_donor = NULL) {
+    reffed_ptr(ptr, ref_donor).swap(*this);
+  }
+
+  template <class U>
+  reffed_ptr<U> down_cast() {
+    return reffed_ptr<U>(upb::down_cast<U*>(get()));
+  }
+
+  template <class U>
+  reffed_ptr<U> dyn_cast() {
+    return reffed_ptr<U>(upb::dyn_cast<U*>(get()));
+  }
+
+  /* Plain release() is unsafe; if we were the only owner, it would leak the
+   * object.  Instead we provide this: */
+  T* ReleaseTo(const void* new_owner) {
+    T* ret = NULL;
+    ptr_->DonateRef(this, new_owner);
+    std::swap(ret, ptr_);
+    return ret;
+  }
+
+ private:
+  T* ptr_;
+};
+
+#endif  /* __cplusplus */
+
 #endif  /* UPB_REFCOUNT_H_ */
 
 #ifdef __cplusplus
@@ -1261,12 +1666,17 @@
 class Def;
 class EnumDef;
 class FieldDef;
+class FileDef;
 class MessageDef;
 class OneofDef;
 }
 #endif
 
 UPB_DECLARE_DERIVED_TYPE(upb::Def, upb::RefCounted, upb_def, upb_refcounted)
+UPB_DECLARE_DERIVED_TYPE(upb::OneofDef, upb::RefCounted, upb_oneofdef,
+                         upb_refcounted)
+UPB_DECLARE_DERIVED_TYPE(upb::FileDef, upb::RefCounted, upb_filedef,
+                         upb_refcounted)
 
 /* The maximum message depth that the type graph can have.  This is a resource
  * limit for the C stack since we sometimes need to recursively traverse the
@@ -1278,15 +1688,16 @@
 #define UPB_MAX_MESSAGE_DEPTH 64
 
 
-/* upb::Def: base class for defs  *********************************************/
+/* upb::Def: base class for top-level defs  ***********************************/
 
-/* All the different kind of defs we support.  These correspond 1:1 with
- * declarations in a .proto file. */
+/* All the different kind of defs that can be defined at the top-level and put
+ * in a SymbolTable or appear in a FileDef::defs() list.  This excludes some
+ * defs (like oneofs and files).  It only includes fields because they can be
+ * defined as extensions. */
 typedef enum {
   UPB_DEF_MSG,
   UPB_DEF_FIELD,
   UPB_DEF_ENUM,
-  UPB_DEF_ONEOF,
   UPB_DEF_SERVICE,   /* Not yet implemented. */
   UPB_DEF_ANY = -1   /* Wildcard for upb_symtab_get*() */
 } upb_deftype_t;
@@ -1309,6 +1720,9 @@
   /* "fullname" is the def's fully-qualified name (eg. foo.bar.Message). */
   const char *full_name() const;
 
+  /* The final part of a def's name (eg. Message). */
+  const char *name() const;
+
   /* The def must be mutable.  Caller retains ownership of fullname.  Defs are
    * not required to have a name; if a def has no name when it is frozen, it
    * will remain an anonymous def.  On failure, returns false and details in "s"
@@ -1316,6 +1730,11 @@
   bool set_full_name(const char* fullname, upb::Status* s);
   bool set_full_name(const std::string &fullname, upb::Status* s);
 
+  /* The file in which this def appears.  It is not necessary to add a def to a
+   * file (and consequently the accessor may return NULL).  Set this by calling
+   * file->Add(def). */
+  FileDef* file() const;
+
   /* Freezes the given defs; this validates all constraints and marks the defs
    * as frozen (read-only).  "defs" may not contain any fielddefs, but fields
    * of any msgdefs will be frozen.
@@ -1327,7 +1746,7 @@
    *
    * After this operation succeeds, the finalized defs must only be accessed
    * through a const pointer! */
-  static bool Freeze(Def* const* defs, int n, Status* status);
+  static bool Freeze(Def* const* defs, size_t n, Status* status);
   static bool Freeze(const std::vector<Def*>& defs, Status* status);
 
  private:
@@ -1346,8 +1765,13 @@
 
 upb_deftype_t upb_def_type(const upb_def *d);
 const char *upb_def_fullname(const upb_def *d);
+const char *upb_def_name(const upb_def *d);
+const upb_filedef *upb_def_file(const upb_def *d);
 bool upb_def_setfullname(upb_def *def, const char *fullname, upb_status *s);
-bool upb_def_freeze(upb_def *const *defs, int n, upb_status *s);
+bool upb_def_freeze(upb_def *const *defs, size_t n, upb_status *s);
+
+/* Temporary API: for internal use only. */
+bool _upb_def_validate(upb_def *const*defs, size_t n, upb_status *s);
 
 UPB_END_EXTERN_C
 
@@ -1396,7 +1820,7 @@
     return (upb_##lower *)def;                                             \
   }                                                                        \
   UPB_INLINE const upb_##lower *upb_downcast_##lower(const upb_def *def) { \
-    assert(upb_def_type(def) == UPB_DEF_##upper);                          \
+    UPB_ASSERT(upb_def_type(def) == UPB_DEF_##upper);                          \
     return (const upb_##lower *)def;                                       \
   }                                                                        \
   UPB_INLINE upb_##lower *upb_dyncast_##lower##_mutable(upb_def *def) {    \
@@ -1420,7 +1844,6 @@
 UPB_DECLARE_DEF_TYPE(upb::FieldDef, fielddef, FIELD)
 UPB_DECLARE_DEF_TYPE(upb::MessageDef, msgdef, MSG)
 UPB_DECLARE_DEF_TYPE(upb::EnumDef, enumdef, ENUM)
-UPB_DECLARE_DEF_TYPE(upb::OneofDef, oneofdef, ONEOF)
 
 #undef UPB_DECLARE_DEF_TYPE
 #undef UPB_DEF_CASTS
@@ -1483,6 +1906,11 @@
   UPB_DESCRIPTOR_TYPE_SINT64   = 18
 } upb_descriptortype_t;
 
+typedef enum {
+  UPB_SYNTAX_PROTO2 = 2,
+  UPB_SYNTAX_PROTO3 = 3
+} upb_syntax_t;
+
 /* Maximum field number allowed for FieldDefs.  This is an inherent limit of the
  * protobuf wire format. */
 #define UPB_MAX_FIELDNUMBER ((1 << 29) - 1)
@@ -1537,6 +1965,27 @@
   uint32_t number() const;   /* Returns 0 if uninitialized. */
   bool is_extension() const;
 
+  /* Copies the JSON name for this field into the given buffer.  Returns the
+   * actual size of the JSON name, including the NULL terminator.  If the
+   * return value is 0, the JSON name is unset.  If the return value is
+   * greater than len, the JSON name was truncated.  The buffer is always
+   * NULL-terminated if len > 0.
+   *
+   * The JSON name always defaults to a camelCased version of the regular
+   * name.  However if the regular name is unset, the JSON name will be unset
+   * also.
+   */
+  size_t GetJsonName(char* buf, size_t len) const;
+
+  /* Convenience version of the above function which copies the JSON name
+   * into the given string, returning false if the name is not set. */
+  template <class T>
+  bool GetJsonName(T* str) {
+    str->resize(GetJsonName(NULL, 0));
+    GetJsonName(&(*str)[0], str->size());
+    return str->size() > 0;
+  }
+
   /* For UPB_TYPE_MESSAGE fields only where is_tag_delimited() == false,
    * indicates whether this field should have lazy parsing handlers that yield
    * the unparsed string for the submessage.
@@ -1557,7 +2006,7 @@
    * whatever message this field belongs to.  Guaranteed to be less than
    * f->containing_type()->field_count().  May only be accessed once the def has
    * been finalized. */
-  int index() const;
+  uint32_t index() const;
 
   /* The MessageDef to which this field belongs.
    *
@@ -1589,6 +2038,18 @@
   bool IsPrimitive() const;
   bool IsMap() const;
 
+  /* Whether this field must be able to explicitly represent presence:
+   *
+   * * This is always false for repeated fields (an empty repeated field is
+   *   equivalent to a repeated field with zero entries).
+   *
+   * * This is always true for submessages.
+   *
+   * * For other fields, it depends on the message (see
+   *   MessageDef::SetPrimitivesHavePresence())
+   */
+  bool HasPresence() const;
+
   /* How integers are encoded.  Only meaningful for integer types.
    * Defaults to UPB_INTFMT_VARIABLE, and is reset when "type" changes. */
   IntegerFormat integer_format() const;
@@ -1690,6 +2151,16 @@
   bool set_name(const char* name, upb::Status* s);
   bool set_name(const std::string& name, upb::Status* s);
 
+  /* Sets the JSON name to the given string. */
+  /* TODO(haberman): implement.  Right now only default json_name (camelCase)
+   * is supported. */
+  bool set_json_name(const char* json_name, upb::Status* s);
+  bool set_json_name(const std::string& name, upb::Status* s);
+
+  /* Clears the JSON name. This will make it revert to its default, which is
+   * a camelCased version of the regular field name. */
+  void clear_json_name();
+
   void set_integer_format(IntegerFormat format);
   bool set_tag_delimited(bool tag_delimited, upb::Status* s);
 
@@ -1754,6 +2225,7 @@
 bool upb_fielddef_isextension(const upb_fielddef *f);
 bool upb_fielddef_lazy(const upb_fielddef *f);
 bool upb_fielddef_packed(const upb_fielddef *f);
+size_t upb_fielddef_getjsonname(const upb_fielddef *f, char *buf, size_t len);
 const upb_msgdef *upb_fielddef_containingtype(const upb_fielddef *f);
 const upb_oneofdef *upb_fielddef_containingoneof(const upb_fielddef *f);
 upb_msgdef *upb_fielddef_containingtype_mutable(upb_fielddef *f);
@@ -1766,6 +2238,7 @@
 bool upb_fielddef_isseq(const upb_fielddef *f);
 bool upb_fielddef_isprimitive(const upb_fielddef *f);
 bool upb_fielddef_ismap(const upb_fielddef *f);
+bool upb_fielddef_haspresence(const upb_fielddef *f);
 int64_t upb_fielddef_defaultint64(const upb_fielddef *f);
 int32_t upb_fielddef_defaultint32(const upb_fielddef *f);
 uint64_t upb_fielddef_defaultuint64(const upb_fielddef *f);
@@ -1787,6 +2260,8 @@
 void upb_fielddef_setlabel(upb_fielddef *f, upb_label_t label);
 bool upb_fielddef_setnumber(upb_fielddef *f, uint32_t number, upb_status *s);
 bool upb_fielddef_setname(upb_fielddef *f, const char *name, upb_status *s);
+bool upb_fielddef_setjsonname(upb_fielddef *f, const char *name, upb_status *s);
+bool upb_fielddef_clearjsonname(upb_fielddef *f);
 bool upb_fielddef_setcontainingtypename(upb_fielddef *f, const char *name,
                                         upb_status *s);
 void upb_fielddef_setisextension(upb_fielddef *f, bool is_extension);
@@ -1827,6 +2302,10 @@
 typedef upb_inttable_iter upb_msg_field_iter;
 typedef upb_strtable_iter upb_msg_oneof_iter;
 
+/* Well-known field tag numbers for map-entry messages. */
+#define UPB_MAPENTRY_KEY   1
+#define UPB_MAPENTRY_VALUE 2
+
 #ifdef __cplusplus
 
 /* Structure that describes a single .proto message type.
@@ -1842,6 +2321,7 @@
 
   /* Functionality from upb::Def. */
   const char* full_name() const;
+  const char* name() const;
   bool set_full_name(const char* fullname, Status* s);
   bool set_full_name(const std::string& fullname, Status* s);
 
@@ -1884,6 +2364,16 @@
   bool AddOneof(OneofDef* o, Status* s);
   bool AddOneof(const reffed_ptr<OneofDef>& o, Status* s);
 
+  upb_syntax_t syntax() const;
+
+  /* Returns false if we don't support this syntax value. */
+  bool set_syntax(upb_syntax_t syntax);
+
+  /* Set this to false to indicate that primitive fields should not have
+   * explicit presence information associated with them.  This will affect all
+   * fields added to this message.  Defaults to true. */
+  void SetPrimitivesHavePresence(bool have_presence);
+
   /* These return NULL if the field is not found. */
   FieldDef* FindFieldByNumber(uint32_t number);
   FieldDef* FindFieldByName(const char *name, size_t len);
@@ -2069,14 +2559,20 @@
 
 bool upb_msgdef_freeze(upb_msgdef *m, upb_status *status);
 
-const char *upb_msgdef_fullname(const upb_msgdef *m);
-bool upb_msgdef_setfullname(upb_msgdef *m, const char *fullname, upb_status *s);
-
 upb_msgdef *upb_msgdef_dup(const upb_msgdef *m, const void *owner);
+const char *upb_msgdef_fullname(const upb_msgdef *m);
+const char *upb_msgdef_name(const upb_msgdef *m);
+int upb_msgdef_numoneofs(const upb_msgdef *m);
+upb_syntax_t upb_msgdef_syntax(const upb_msgdef *m);
+
 bool upb_msgdef_addfield(upb_msgdef *m, upb_fielddef *f, const void *ref_donor,
                          upb_status *s);
 bool upb_msgdef_addoneof(upb_msgdef *m, upb_oneofdef *o, const void *ref_donor,
                          upb_status *s);
+bool upb_msgdef_setfullname(upb_msgdef *m, const char *fullname, upb_status *s);
+void upb_msgdef_setmapentry(upb_msgdef *m, bool map_entry);
+bool upb_msgdef_mapentry(const upb_msgdef *m);
+bool upb_msgdef_setsyntax(upb_msgdef *m, upb_syntax_t syntax);
 
 /* Field lookup in a couple of different variations:
  *   - itof = int to field
@@ -2118,18 +2614,21 @@
   return (upb_oneofdef *)upb_msgdef_ntoo(m, name, len);
 }
 
-void upb_msgdef_setmapentry(upb_msgdef *m, bool map_entry);
-bool upb_msgdef_mapentry(const upb_msgdef *m);
+/* Lookup of either field or oneof by name.  Returns whether either was found.
+ * If the return is true, then the found def will be set, and the non-found
+ * one set to NULL. */
+bool upb_msgdef_lookupname(const upb_msgdef *m, const char *name, size_t len,
+                           const upb_fielddef **f, const upb_oneofdef **o);
 
-/* Well-known field tag numbers for map-entry messages. */
-#define UPB_MAPENTRY_KEY   1
-#define UPB_MAPENTRY_VALUE 2
+UPB_INLINE bool upb_msgdef_lookupnamez(const upb_msgdef *m, const char *name,
+                                       const upb_fielddef **f,
+                                       const upb_oneofdef **o) {
+  return upb_msgdef_lookupname(m, name, strlen(name), f, o);
+}
 
-const upb_oneofdef *upb_msgdef_findoneof(const upb_msgdef *m,
-                                          const char *name);
-int upb_msgdef_numoneofs(const upb_msgdef *m);
-
-/* upb_msg_field_iter i;
+/* Iteration over fields and oneofs.  For example:
+ *
+ * upb_msg_field_iter i;
  * for(upb_msg_field_begin(&i, m);
  *     !upb_msg_field_done(&i);
  *     upb_msg_field_next(&i)) {
@@ -2175,6 +2674,7 @@
 
   /* Functionality from upb::Def. */
   const char* full_name() const;
+  const char* name() const;
   bool set_full_name(const char* fullname, Status* s);
   bool set_full_name(const std::string& fullname, Status* s);
 
@@ -2249,6 +2749,7 @@
 
 /* From upb_def. */
 const char *upb_enumdef_fullname(const upb_enumdef *e);
+const char *upb_enumdef_name(const upb_enumdef *e);
 bool upb_enumdef_setfullname(upb_enumdef *e, const char *fullname,
                              upb_status *s);
 
@@ -2290,8 +2791,7 @@
 
 #ifdef __cplusplus
 
-/* Class that represents a oneof.  Its base class is upb::Def (convert with
- * upb::upcast()). */
+/* Class that represents a oneof. */
 class upb::OneofDef {
  public:
   /* Returns NULL if memory allocation failed. */
@@ -2300,9 +2800,6 @@
   /* upb::RefCounted methods like Ref()/Unref(). */
   UPB_REFCOUNTED_CPPMETHODS
 
-  /* Functionality from upb::Def. */
-  const char* full_name() const;
-
   /* Returns the MessageDef that owns this OneofDef. */
   const MessageDef* containing_type() const;
 
@@ -2310,6 +2807,7 @@
    * by name once added to a message def. */
   const char* name() const;
   bool set_name(const char* name, Status* s);
+  bool set_name(const std::string& name, Status* s);
 
   /* Returns the number of fields currently defined in the oneof. */
   int field_count() const;
@@ -2403,7 +2901,7 @@
 upb_oneofdef *upb_oneofdef_dup(const upb_oneofdef *o, const void *owner);
 
 /* Include upb_refcounted methods like upb_oneofdef_ref(). */
-UPB_REFCOUNTED_CMETHODS(upb_oneofdef, upb_oneofdef_upcast2)
+UPB_REFCOUNTED_CMETHODS(upb_oneofdef, upb_oneofdef_upcast)
 
 const char *upb_oneofdef_name(const upb_oneofdef *o);
 bool upb_oneofdef_setname(upb_oneofdef *o, const char *name, upb_status *s);
@@ -2439,10 +2937,124 @@
 
 UPB_END_EXTERN_C
 
+
+/* upb::FileDef ***************************************************************/
+
+#ifdef __cplusplus
+
+/* Class that represents a .proto file with some things defined in it.
+ *
+ * Many users won't care about FileDefs, but they are necessary if you want to
+ * read the values of file-level options. */
+class upb::FileDef {
+ public:
+  /* Returns NULL if memory allocation failed. */
+  static reffed_ptr<FileDef> New();
+
+  /* upb::RefCounted methods like Ref()/Unref(). */
+  UPB_REFCOUNTED_CPPMETHODS
+
+  /* Get/set name of the file (eg. "foo/bar.proto"). */
+  const char* name() const;
+  bool set_name(const char* name, Status* s);
+  bool set_name(const std::string& name, Status* s);
+
+  /* Package name for definitions inside the file (eg. "foo.bar"). */
+  const char* package() const;
+  bool set_package(const char* package, Status* s);
+
+  /* Syntax for the file.  Defaults to proto2. */
+  upb_syntax_t syntax() const;
+  void set_syntax(upb_syntax_t syntax);
+
+  /* Get the list of defs from the file.  These are returned in the order that
+   * they were added to the FileDef. */
+  int def_count() const;
+  const Def* def(int index) const;
+  Def* def(int index);
+
+  /* Get the list of dependencies from the file.  These are returned in the
+   * order that they were added to the FileDef. */
+  int dependency_count() const;
+  const FileDef* dependency(int index) const;
+
+  /* Adds defs to this file.  The def must not already belong to another
+   * file.
+   *
+   * Note: this does *not* ensure that this def's name is unique in this file!
+   * Use a SymbolTable if you want to check this property.  Especially since
+   * properly checking uniqueness would require a check across *all* files
+   * (including dependencies). */
+  bool AddDef(Def* def, Status* s);
+  bool AddMessage(MessageDef* m, Status* s);
+  bool AddEnum(EnumDef* e, Status* s);
+  bool AddExtension(FieldDef* f, Status* s);
+
+  /* Adds a dependency of this file. */
+  bool AddDependency(const FileDef* file);
+
+  /* Freezes this FileDef and all messages/enums under it.  All subdefs must be
+   * resolved and all messages/enums must validate.  Returns true if this
+   * succeeded.
+   *
+   * TODO(haberman): should we care whether the file's dependencies are frozen
+   * already? */
+  bool Freeze(Status* s);
+
+ private:
+  UPB_DISALLOW_POD_OPS(FileDef, upb::FileDef)
+};
+
+#endif
+
+UPB_BEGIN_EXTERN_C
+
+upb_filedef *upb_filedef_new(const void *owner);
+
+/* Include upb_refcounted methods like upb_msgdef_ref(). */
+UPB_REFCOUNTED_CMETHODS(upb_filedef, upb_filedef_upcast)
+
+const char *upb_filedef_name(const upb_filedef *f);
+const char *upb_filedef_package(const upb_filedef *f);
+upb_syntax_t upb_filedef_syntax(const upb_filedef *f);
+size_t upb_filedef_defcount(const upb_filedef *f);
+size_t upb_filedef_depcount(const upb_filedef *f);
+const upb_def *upb_filedef_def(const upb_filedef *f, size_t i);
+const upb_filedef *upb_filedef_dep(const upb_filedef *f, size_t i);
+
+bool upb_filedef_freeze(upb_filedef *f, upb_status *s);
+bool upb_filedef_setname(upb_filedef *f, const char *name, upb_status *s);
+bool upb_filedef_setpackage(upb_filedef *f, const char *package, upb_status *s);
+bool upb_filedef_setsyntax(upb_filedef *f, upb_syntax_t syntax, upb_status *s);
+
+bool upb_filedef_adddef(upb_filedef *f, upb_def *def, const void *ref_donor,
+                        upb_status *s);
+bool upb_filedef_adddep(upb_filedef *f, const upb_filedef *dep);
+
+UPB_INLINE bool upb_filedef_addmsg(upb_filedef *f, upb_msgdef *m,
+                                   const void *ref_donor, upb_status *s) {
+  return upb_filedef_adddef(f, upb_msgdef_upcast_mutable(m), ref_donor, s);
+}
+
+UPB_INLINE bool upb_filedef_addenum(upb_filedef *f, upb_enumdef *e,
+                                    const void *ref_donor, upb_status *s) {
+  return upb_filedef_adddef(f, upb_enumdef_upcast_mutable(e), ref_donor, s);
+}
+
+UPB_INLINE bool upb_filedef_addext(upb_filedef *file, upb_fielddef *f,
+                                   const void *ref_donor, upb_status *s) {
+  return upb_filedef_adddef(file, upb_fielddef_upcast_mutable(f), ref_donor, s);
+}
+UPB_INLINE upb_def *upb_filedef_mutabledef(upb_filedef *f, int i) {
+  return (upb_def*)upb_filedef_def(f, i);
+}
+
+UPB_END_EXTERN_C
+
 #ifdef __cplusplus
 
 UPB_INLINE const char* upb_safecstr(const std::string& str) {
-  assert(str.size() == std::strlen(str.c_str()));
+  UPB_ASSERT(str.size() == std::strlen(str.c_str()));
   return str.c_str();
 }
 
@@ -2454,13 +3066,14 @@
 }
 inline Def::Type Def::def_type() const { return upb_def_type(this); }
 inline const char* Def::full_name() const { return upb_def_fullname(this); }
+inline const char* Def::name() const { return upb_def_name(this); }
 inline bool Def::set_full_name(const char* fullname, Status* s) {
   return upb_def_setfullname(this, fullname, s);
 }
 inline bool Def::set_full_name(const std::string& fullname, Status* s) {
   return upb_def_setfullname(this, upb_safecstr(fullname), s);
 }
-inline bool Def::Freeze(Def* const* defs, int n, Status* status) {
+inline bool Def::Freeze(Def* const* defs, size_t n, Status* status) {
   return upb_def_freeze(defs, n, status);
 }
 inline bool Def::Freeze(const std::vector<Def*>& defs, Status* status) {
@@ -2480,19 +3093,19 @@
   return upb_fielddef_checkintfmt(val);
 }
 inline FieldDef::Type FieldDef::ConvertType(int32_t val) {
-  assert(CheckType(val));
+  UPB_ASSERT(CheckType(val));
   return static_cast<FieldDef::Type>(val);
 }
 inline FieldDef::Label FieldDef::ConvertLabel(int32_t val) {
-  assert(CheckLabel(val));
+  UPB_ASSERT(CheckLabel(val));
   return static_cast<FieldDef::Label>(val);
 }
 inline FieldDef::DescriptorType FieldDef::ConvertDescriptorType(int32_t val) {
-  assert(CheckDescriptorType(val));
+  UPB_ASSERT(CheckDescriptorType(val));
   return static_cast<FieldDef::DescriptorType>(val);
 }
 inline FieldDef::IntegerFormat FieldDef::ConvertIntegerFormat(int32_t val) {
-  assert(CheckIntegerFormat(val));
+  UPB_ASSERT(CheckIntegerFormat(val));
   return static_cast<FieldDef::IntegerFormat>(val);
 }
 
@@ -2527,6 +3140,9 @@
 inline bool FieldDef::is_extension() const {
   return upb_fielddef_isextension(this);
 }
+inline size_t FieldDef::GetJsonName(char* buf, size_t len) const {
+  return upb_fielddef_getjsonname(this, buf, len);
+}
 inline bool FieldDef::lazy() const {
   return upb_fielddef_lazy(this);
 }
@@ -2536,6 +3152,9 @@
 inline bool FieldDef::packed() const {
   return upb_fielddef_packed(this);
 }
+inline uint32_t FieldDef::index() const {
+  return upb_fielddef_index(this);
+}
 inline void FieldDef::set_packed(bool packed) {
   upb_fielddef_setpacked(this, packed);
 }
@@ -2557,6 +3176,15 @@
 inline bool FieldDef::set_name(const std::string& name, Status* s) {
   return upb_fielddef_setname(this, upb_safecstr(name), s);
 }
+inline bool FieldDef::set_json_name(const char *name, Status* s) {
+  return upb_fielddef_setjsonname(this, name, s);
+}
+inline bool FieldDef::set_json_name(const std::string& name, Status* s) {
+  return upb_fielddef_setjsonname(this, upb_safecstr(name), s);
+}
+inline void FieldDef::clear_json_name() {
+  upb_fielddef_clearjsonname(this);
+}
 inline bool FieldDef::set_containing_type_name(const char *name, Status* s) {
   return upb_fielddef_setcontainingtypename(this, name, s);
 }
@@ -2671,12 +3299,21 @@
 inline const char *MessageDef::full_name() const {
   return upb_msgdef_fullname(this);
 }
+inline const char *MessageDef::name() const {
+  return upb_msgdef_name(this);
+}
+inline upb_syntax_t MessageDef::syntax() const {
+  return upb_msgdef_syntax(this);
+}
 inline bool MessageDef::set_full_name(const char* fullname, Status* s) {
   return upb_msgdef_setfullname(this, fullname, s);
 }
 inline bool MessageDef::set_full_name(const std::string& fullname, Status* s) {
   return upb_msgdef_setfullname(this, upb_safecstr(fullname), s);
 }
+inline bool MessageDef::set_syntax(upb_syntax_t syntax) {
+  return upb_msgdef_setsyntax(this, syntax);
+}
 inline bool MessageDef::Freeze(Status* status) {
   return upb_msgdef_freeze(this, status);
 }
@@ -2858,6 +3495,9 @@
 inline const char* EnumDef::full_name() const {
   return upb_enumdef_fullname(this);
 }
+inline const char* EnumDef::name() const {
+  return upb_enumdef_name(this);
+}
 inline bool EnumDef::set_full_name(const char* fullname, Status* s) {
   return upb_enumdef_setfullname(this, fullname, s);
 }
@@ -2907,9 +3547,6 @@
   upb_oneofdef *o = upb_oneofdef_new(&o);
   return reffed_ptr<OneofDef>(o, &o);
 }
-inline const char* OneofDef::full_name() const {
-  return upb_oneofdef_name(this);
-}
 
 inline const MessageDef* OneofDef::containing_type() const {
   return upb_oneofdef_containingtype(this);
@@ -2920,6 +3557,9 @@
 inline bool OneofDef::set_name(const char* name, Status* s) {
   return upb_oneofdef_setname(this, name, s);
 }
+inline bool OneofDef::set_name(const std::string& name, Status* s) {
+  return upb_oneofdef_setname(this, upb_safecstr(name), s);
+}
 inline int OneofDef::field_count() const {
   return upb_oneofdef_numfields(this);
 }
@@ -2988,6 +3628,57 @@
   return !(*this == other);
 }
 
+inline reffed_ptr<FileDef> FileDef::New() {
+  upb_filedef *f = upb_filedef_new(&f);
+  return reffed_ptr<FileDef>(f, &f);
+}
+
+inline const char* FileDef::name() const {
+  return upb_filedef_name(this);
+}
+inline bool FileDef::set_name(const char* name, Status* s) {
+  return upb_filedef_setname(this, name, s);
+}
+inline bool FileDef::set_name(const std::string& name, Status* s) {
+  return upb_filedef_setname(this, upb_safecstr(name), s);
+}
+inline const char* FileDef::package() const {
+  return upb_filedef_package(this);
+}
+inline bool FileDef::set_package(const char* package, Status* s) {
+  return upb_filedef_setpackage(this, package, s);
+}
+inline int FileDef::def_count() const {
+  return upb_filedef_defcount(this);
+}
+inline const Def* FileDef::def(int index) const {
+  return upb_filedef_def(this, index);
+}
+inline Def* FileDef::def(int index) {
+  return const_cast<Def*>(upb_filedef_def(this, index));
+}
+inline int FileDef::dependency_count() const {
+  return upb_filedef_depcount(this);
+}
+inline const FileDef* FileDef::dependency(int index) const {
+  return upb_filedef_dep(this, index);
+}
+inline bool FileDef::AddDef(Def* def, Status* s) {
+  return upb_filedef_adddef(this, def, NULL, s);
+}
+inline bool FileDef::AddMessage(MessageDef* m, Status* s) {
+  return upb_filedef_addmsg(this, m, NULL, s);
+}
+inline bool FileDef::AddEnum(EnumDef* e, Status* s) {
+  return upb_filedef_addenum(this, e, NULL, s);
+}
+inline bool FileDef::AddExtension(FieldDef* f, Status* s) {
+  return upb_filedef_addext(this, f, NULL, s);
+}
+inline bool FileDef::AddDependency(const FileDef* file) {
+  return upb_filedef_adddep(this, file);
+}
+
 }  /* namespace upb */
 #endif
 
@@ -3026,6 +3717,7 @@
   upb_refcounted base;
 
   const char *fullname;
+  const upb_filedef* file;
   char type;  /* A upb_deftype_t (char to save space) */
 
   /* Used as a flag during the def's mutable stage.  Must be false unless
@@ -3035,8 +3727,8 @@
   bool came_from_user;
 };
 
-#define UPB_DEF_INIT(name, type, refs, ref2s) \
-    { UPB_REFCOUNT_INIT(refs, ref2s), name, type, false }
+#define UPB_DEF_INIT(name, type, vtbl, refs, ref2s) \
+    { UPB_REFCOUNT_INIT(vtbl, refs, ref2s), name, NULL, type, false }
 
 
 /* upb_fielddef ***************************************************************/
@@ -3076,12 +3768,14 @@
   uint32_t index_;
 };
 
+extern const struct upb_refcounted_vtbl upb_fielddef_vtbl;
+
 #define UPB_FIELDDEF_INIT(label, type, intfmt, tagdelim, is_extension, lazy,   \
                           packed, name, num, msgdef, subdef, selector_base,    \
                           index, defaultval, refs, ref2s)                      \
   {                                                                            \
-    UPB_DEF_INIT(name, UPB_DEF_FIELD, refs, ref2s), defaultval, {msgdef},      \
-        {subdef}, NULL, false, false,                                          \
+    UPB_DEF_INIT(name, UPB_DEF_FIELD, &upb_fielddef_vtbl, refs, ref2s),        \
+        defaultval, {msgdef}, {subdef}, NULL, false, false,                    \
         type == UPB_TYPE_STRING || type == UPB_TYPE_BYTES, true, is_extension, \
         lazy, packed, intfmt, tagdelim, type, label, num, selector_base, index \
   }
@@ -3097,27 +3791,26 @@
 
   /* Tables for looking up fields by number and name. */
   upb_inttable itof;  /* int to field */
-  upb_strtable ntof;  /* name to field */
+  upb_strtable ntof;  /* name to field/oneof */
 
-  /* Tables for looking up oneofs by name. */
-  upb_strtable ntoo;  /* name to oneof */
-
-  /* Is this a map-entry message?
-   * TODO: set this flag properly for static descriptors; regenerate
-   * descriptor.upb.c. */
+  /* Is this a map-entry message? */
   bool map_entry;
 
+  /* Whether this message has proto2 or proto3 semantics. */
+  upb_syntax_t syntax;
+
   /* TODO(haberman): proper extension ranges (there can be multiple). */
 };
 
+extern const struct upb_refcounted_vtbl upb_msgdef_vtbl;
+
 /* TODO: also support static initialization of the oneofs table. This will be
  * needed if we compile in descriptors that contain oneofs. */
 #define UPB_MSGDEF_INIT(name, selector_count, submsg_field_count, itof, ntof, \
-                        refs, ref2s)                                          \
+                        map_entry, syntax, refs, ref2s)                       \
   {                                                                           \
-    UPB_DEF_INIT(name, UPB_DEF_MSG, refs, ref2s), selector_count,             \
-        submsg_field_count, itof, ntof,                                       \
-        UPB_EMPTY_STRTABLE_INIT(UPB_CTYPE_PTR), false                         \
+    UPB_DEF_INIT(name, UPB_DEF_MSG, &upb_fielddef_vtbl, refs, ref2s),         \
+        selector_count, submsg_field_count, itof, ntof, map_entry, syntax     \
   }
 
 
@@ -3131,22 +3824,28 @@
   int32_t defaultval;
 };
 
+extern const struct upb_refcounted_vtbl upb_enumdef_vtbl;
+
 #define UPB_ENUMDEF_INIT(name, ntoi, iton, defaultval, refs, ref2s) \
-  { UPB_DEF_INIT(name, UPB_DEF_ENUM, refs, ref2s), ntoi, iton, defaultval }
+  { UPB_DEF_INIT(name, UPB_DEF_ENUM, &upb_enumdef_vtbl, refs, ref2s), ntoi,    \
+    iton, defaultval }
 
 
 /* upb_oneofdef ***************************************************************/
 
 struct upb_oneofdef {
-  upb_def base;
+  upb_refcounted base;
 
+  const char *name;
   upb_strtable ntof;
   upb_inttable itof;
   const upb_msgdef *parent;
 };
 
+extern const struct upb_refcounted_vtbl upb_oneofdef_vtbl;
+
 #define UPB_ONEOFDEF_INIT(name, ntof, itof, refs, ref2s) \
-  { UPB_DEF_INIT(name, UPB_DEF_ENUM, refs, ref2s), ntof, itof }
+  { UPB_REFCOUNT_INIT(&upb_oneofdef_vtbl, refs, ref2s), name, ntof, itof }
 
 
 /* upb_symtab *****************************************************************/
@@ -3157,9 +3856,18 @@
   upb_strtable symtab;
 };
 
-#define UPB_SYMTAB_INIT(symtab, refs, ref2s) \
-  { UPB_REFCOUNT_INIT(refs, ref2s), symtab }
+struct upb_filedef {
+  upb_refcounted base;
 
+  const char *name;
+  const char *package;
+  upb_syntax_t syntax;
+
+  upb_inttable defs;
+  upb_inttable deps;
+};
+
+extern const struct upb_refcounted_vtbl upb_filedef_vtbl;
 
 #endif  /* UPB_STATICINIT_H_ */
 /*
@@ -3773,7 +4481,7 @@
   void AddCleanup(Handlers* h) const {
     if (cleanup_func_) {
       bool ok = h->AddCleanup(cleanup_data_, cleanup_func_);
-      UPB_ASSERT_VAR(ok, ok);
+      UPB_ASSERT(ok);
     }
   }
 
@@ -4793,7 +5501,7 @@
   inline bool Handlers::SetValueHandler<vtype>(                                \
       const FieldDef *f,                                                       \
       const Handlers::utype ## Handler& handler) {                             \
-    assert(!handler.registered_);                                              \
+    UPB_ASSERT(!handler.registered_);                                              \
     handler.AddCleanup(this);                                                  \
     handler.registered_ = true;                                                \
     return upb_handlers_set##ltype(this, f, handler.handler_, &handler.attr_); \
@@ -4905,7 +5613,7 @@
 
 template <class T>
 inline Handler<T>::~Handler() {
-  assert(registered_);
+  UPB_ASSERT(registered_);
 }
 
 inline HandlerAttributes::HandlerAttributes() { upb_handlerattr_init(this); }
@@ -4991,63 +5699,63 @@
 }
 inline bool Handlers::SetStartMessageHandler(
     const Handlers::StartMessageHandler &handler) {
-  assert(!handler.registered_);
+  UPB_ASSERT(!handler.registered_);
   handler.registered_ = true;
   handler.AddCleanup(this);
   return upb_handlers_setstartmsg(this, handler.handler_, &handler.attr_);
 }
 inline bool Handlers::SetEndMessageHandler(
     const Handlers::EndMessageHandler &handler) {
-  assert(!handler.registered_);
+  UPB_ASSERT(!handler.registered_);
   handler.registered_ = true;
   handler.AddCleanup(this);
   return upb_handlers_setendmsg(this, handler.handler_, &handler.attr_);
 }
 inline bool Handlers::SetStartStringHandler(const FieldDef *f,
                                             const StartStringHandler &handler) {
-  assert(!handler.registered_);
+  UPB_ASSERT(!handler.registered_);
   handler.registered_ = true;
   handler.AddCleanup(this);
   return upb_handlers_setstartstr(this, f, handler.handler_, &handler.attr_);
 }
 inline bool Handlers::SetEndStringHandler(const FieldDef *f,
                                           const EndFieldHandler &handler) {
-  assert(!handler.registered_);
+  UPB_ASSERT(!handler.registered_);
   handler.registered_ = true;
   handler.AddCleanup(this);
   return upb_handlers_setendstr(this, f, handler.handler_, &handler.attr_);
 }
 inline bool Handlers::SetStringHandler(const FieldDef *f,
                                        const StringHandler& handler) {
-  assert(!handler.registered_);
+  UPB_ASSERT(!handler.registered_);
   handler.registered_ = true;
   handler.AddCleanup(this);
   return upb_handlers_setstring(this, f, handler.handler_, &handler.attr_);
 }
 inline bool Handlers::SetStartSequenceHandler(
     const FieldDef *f, const StartFieldHandler &handler) {
-  assert(!handler.registered_);
+  UPB_ASSERT(!handler.registered_);
   handler.registered_ = true;
   handler.AddCleanup(this);
   return upb_handlers_setstartseq(this, f, handler.handler_, &handler.attr_);
 }
 inline bool Handlers::SetStartSubMessageHandler(
     const FieldDef *f, const StartFieldHandler &handler) {
-  assert(!handler.registered_);
+  UPB_ASSERT(!handler.registered_);
   handler.registered_ = true;
   handler.AddCleanup(this);
   return upb_handlers_setstartsubmsg(this, f, handler.handler_, &handler.attr_);
 }
 inline bool Handlers::SetEndSubMessageHandler(const FieldDef *f,
                                               const EndFieldHandler &handler) {
-  assert(!handler.registered_);
+  UPB_ASSERT(!handler.registered_);
   handler.registered_ = true;
   handler.AddCleanup(this);
   return upb_handlers_setendsubmsg(this, f, handler.handler_, &handler.attr_);
 }
 inline bool Handlers::SetEndSequenceHandler(const FieldDef *f,
                                             const EndFieldHandler &handler) {
-  assert(!handler.registered_);
+  UPB_ASSERT(!handler.registered_);
   handler.registered_ = true;
   handler.AddCleanup(this);
   return upb_handlers_setendseq(this, f, handler.handler_, &handler.attr_);
@@ -5102,267 +5810,6 @@
 
 #endif  /* UPB_HANDLERS_H */
 /*
-** upb::Environment (upb_env)
-**
-** A upb::Environment provides a means for injecting malloc and an
-** error-reporting callback into encoders/decoders.  This allows them to be
-** independent of nearly all assumptions about their actual environment.
-**
-** It is also a container for allocating the encoders/decoders themselves that
-** insulates clients from knowing their actual size.  This provides ABI
-** compatibility even if the size of the objects change.  And this allows the
-** structure definitions to be in the .c files instead of the .h files, making
-** the .h files smaller and more readable.
-*/
-
-
-#ifndef UPB_ENV_H_
-#define UPB_ENV_H_
-
-#ifdef __cplusplus
-namespace upb {
-class Environment;
-class SeededAllocator;
-}
-#endif
-
-UPB_DECLARE_TYPE(upb::Environment, upb_env)
-UPB_DECLARE_TYPE(upb::SeededAllocator, upb_seededalloc)
-
-typedef void *upb_alloc_func(void *ud, void *ptr, size_t oldsize, size_t size);
-typedef void upb_cleanup_func(void *ud);
-typedef bool upb_error_func(void *ud, const upb_status *status);
-
-#ifdef __cplusplus
-
-/* An environment is *not* thread-safe. */
-class upb::Environment {
- public:
-  Environment();
-  ~Environment();
-
-  /* Set a custom memory allocation function for the environment.  May ONLY
-   * be called before any calls to Malloc()/Realloc()/AddCleanup() below.
-   * If this is not called, the system realloc() function will be used.
-   * The given user pointer "ud" will be passed to the allocation function.
-   *
-   * The allocation function will not receive corresponding "free" calls.  it
-   * must ensure that the memory is valid for the lifetime of the Environment,
-   * but it may be reclaimed any time thereafter.  The likely usage is that
-   * "ud" points to a stateful allocator, and that the allocator frees all
-   * memory, arena-style, when it is destroyed.  In this case the allocator must
-   * outlive the Environment.  Another possibility is that the allocation
-   * function returns GC-able memory that is guaranteed to be GC-rooted for the
-   * life of the Environment. */
-  void SetAllocationFunction(upb_alloc_func* alloc, void* ud);
-
-  template<class T>
-  void SetAllocator(T* allocator) {
-    SetAllocationFunction(allocator->GetAllocationFunction(), allocator);
-  }
-
-  /* Set a custom error reporting function. */
-  void SetErrorFunction(upb_error_func* func, void* ud);
-
-  /* Set the error reporting function to simply copy the status to the given
-   * status and abort. */
-  void ReportErrorsTo(Status* status);
-
-  /* Returns true if all allocations and AddCleanup() calls have succeeded,
-   * and no errors were reported with ReportError() (except ones that recovered
-   * successfully). */
-  bool ok() const;
-
-  /* Functions for use by encoders/decoders. **********************************/
-
-  /* Reports an error to this environment's callback, returning true if
-   * the caller should try to recover. */
-  bool ReportError(const Status* status);
-
-  /* Allocate memory.  Uses the environment's allocation function.
-   *
-   * There is no need to free(). All memory will be freed automatically, but is
-   * guaranteed to outlive the Environment. */
-  void* Malloc(size_t size);
-
-  /* Reallocate memory.  Preserves "oldsize" bytes from the existing buffer
-   * Requires: oldsize <= existing_size.
-   *
-   * TODO(haberman): should we also enforce that oldsize <= size? */
-  void* Realloc(void* ptr, size_t oldsize, size_t size);
-
-  /* Add a cleanup function to run when the environment is destroyed.
-   * Returns false on out-of-memory.
-   *
-   * The first call to AddCleanup() after SetAllocationFunction() is guaranteed
-   * to return true -- this makes it possible to robustly set a cleanup handler
-   * for a custom allocation function. */
-  bool AddCleanup(upb_cleanup_func* func, void* ud);
-
-  /* Total number of bytes that have been allocated.  It is undefined what
-   * Realloc() does to this counter. */
-  size_t BytesAllocated() const;
-
- private:
-  UPB_DISALLOW_COPY_AND_ASSIGN(Environment)
-
-#else
-struct upb_env {
-#endif  /* __cplusplus */
-
-  bool ok_;
-  size_t bytes_allocated;
-
-  /* Alloc function. */
-  upb_alloc_func *alloc;
-  void *alloc_ud;
-
-  /* Error-reporting function. */
-  upb_error_func *err;
-  void *err_ud;
-
-  /* Userdata for default alloc func. */
-  void *default_alloc_ud;
-
-  /* Cleanup entries.  Pointer to a cleanup_ent, defined in env.c */
-  void *cleanup_head;
-
-  /* For future expansion, since the size of this struct is exposed to users. */
-  void *future1;
-  void *future2;
-};
-
-UPB_BEGIN_EXTERN_C
-
-void upb_env_init(upb_env *e);
-void upb_env_uninit(upb_env *e);
-void upb_env_setallocfunc(upb_env *e, upb_alloc_func *func, void *ud);
-void upb_env_seterrorfunc(upb_env *e, upb_error_func *func, void *ud);
-void upb_env_reporterrorsto(upb_env *e, upb_status *status);
-bool upb_env_ok(const upb_env *e);
-bool upb_env_reporterror(upb_env *e, const upb_status *status);
-void *upb_env_malloc(upb_env *e, size_t size);
-void *upb_env_realloc(upb_env *e, void *ptr, size_t oldsize, size_t size);
-bool upb_env_addcleanup(upb_env *e, upb_cleanup_func *func, void *ud);
-size_t upb_env_bytesallocated(const upb_env *e);
-
-UPB_END_EXTERN_C
-
-#ifdef __cplusplus
-
-/* An allocator that allocates from an initial memory region (likely the stack)
- * before falling back to another allocator. */
-class upb::SeededAllocator {
- public:
-  SeededAllocator(void *mem, size_t len);
-  ~SeededAllocator();
-
-  /* Set a custom fallback memory allocation function for the allocator, to use
-   * once the initial region runs out.
-   *
-   * May ONLY be called before GetAllocationFunction().  If this is not
-   * called, the system realloc() will be the fallback allocator. */
-  void SetFallbackAllocator(upb_alloc_func *alloc, void *ud);
-
-  /* Gets the allocation function for this allocator. */
-  upb_alloc_func* GetAllocationFunction();
-
- private:
-  UPB_DISALLOW_COPY_AND_ASSIGN(SeededAllocator)
-
-#else
-struct upb_seededalloc {
-#endif  /* __cplusplus */
-
-  /* Fallback alloc function.  */
-  upb_alloc_func *alloc;
-  upb_cleanup_func *alloc_cleanup;
-  void *alloc_ud;
-  bool need_cleanup;
-  bool returned_allocfunc;
-
-  /* Userdata for default alloc func. */
-  void *default_alloc_ud;
-
-  /* Pointers for the initial memory region. */
-  char *mem_base;
-  char *mem_ptr;
-  char *mem_limit;
-
-  /* For future expansion, since the size of this struct is exposed to users. */
-  void *future1;
-  void *future2;
-};
-
-UPB_BEGIN_EXTERN_C
-
-void upb_seededalloc_init(upb_seededalloc *a, void *mem, size_t len);
-void upb_seededalloc_uninit(upb_seededalloc *a);
-void upb_seededalloc_setfallbackalloc(upb_seededalloc *a, upb_alloc_func *func,
-                                      void *ud);
-upb_alloc_func *upb_seededalloc_getallocfunc(upb_seededalloc *a);
-
-UPB_END_EXTERN_C
-
-#ifdef __cplusplus
-
-namespace upb {
-
-inline Environment::Environment() {
-  upb_env_init(this);
-}
-inline Environment::~Environment() {
-  upb_env_uninit(this);
-}
-inline void Environment::SetAllocationFunction(upb_alloc_func *alloc,
-                                               void *ud) {
-  upb_env_setallocfunc(this, alloc, ud);
-}
-inline void Environment::SetErrorFunction(upb_error_func *func, void *ud) {
-  upb_env_seterrorfunc(this, func, ud);
-}
-inline void Environment::ReportErrorsTo(Status* status) {
-  upb_env_reporterrorsto(this, status);
-}
-inline bool Environment::ok() const {
-  return upb_env_ok(this);
-}
-inline bool Environment::ReportError(const Status* status) {
-  return upb_env_reporterror(this, status);
-}
-inline void *Environment::Malloc(size_t size) {
-  return upb_env_malloc(this, size);
-}
-inline void *Environment::Realloc(void *ptr, size_t oldsize, size_t size) {
-  return upb_env_realloc(this, ptr, oldsize, size);
-}
-inline bool Environment::AddCleanup(upb_cleanup_func *func, void *ud) {
-  return upb_env_addcleanup(this, func, ud);
-}
-inline size_t Environment::BytesAllocated() const {
-  return upb_env_bytesallocated(this);
-}
-
-inline SeededAllocator::SeededAllocator(void *mem, size_t len) {
-  upb_seededalloc_init(this, mem, len);
-}
-inline SeededAllocator::~SeededAllocator() {
-  upb_seededalloc_uninit(this);
-}
-inline void SeededAllocator::SetFallbackAllocator(upb_alloc_func *alloc,
-                                                  void *ud) {
-  upb_seededalloc_setfallbackalloc(this, alloc, ud);
-}
-inline upb_alloc_func *SeededAllocator::GetAllocationFunction() {
-  return upb_seededalloc_getallocfunc(this);
-}
-
-}  /* namespace upb */
-
-#endif  /* __cplusplus */
-
-#endif  /* UPB_ENV_H_ */
-/*
 ** upb::Sink (upb_sink)
 ** upb::BytesSink (upb_bytessink)
 **
@@ -6066,12 +6513,17 @@
    * only a few messages are changing.  We may want to add a way of adding a
    * tree of frozen defs to the symtab (perhaps an alternate constructor where
    * you pass the root of the tree?) */
-  bool Add(Def*const* defs, int n, void* ref_donor, upb_status* status);
+  bool Add(Def*const* defs, size_t n, void* ref_donor, Status* status);
 
   bool Add(const std::vector<Def*>& defs, void *owner, Status* status) {
     return Add((Def*const*)&defs[0], defs.size(), owner, status);
   }
 
+  /* Resolves all subdefs for messages in this file and attempts to freeze the
+   * file.  If this succeeds, adds all the symbols to this SymbolTable
+   * (replacing any existing ones with the same names). */
+  bool AddFile(FileDef* file, Status* s);
+
  private:
   UPB_DISALLOW_POD_OPS(SymbolTable, upb::SymbolTable)
 };
@@ -6092,8 +6544,9 @@
 const upb_def *upb_symtab_lookup(const upb_symtab *s, const char *sym);
 const upb_msgdef *upb_symtab_lookupmsg(const upb_symtab *s, const char *sym);
 const upb_enumdef *upb_symtab_lookupenum(const upb_symtab *s, const char *sym);
-bool upb_symtab_add(upb_symtab *s, upb_def *const*defs, int n, void *ref_donor,
-                    upb_status *status);
+bool upb_symtab_add(upb_symtab *s, upb_def *const*defs, size_t n,
+                    void *ref_donor, upb_status *status);
+bool upb_symtab_addfile(upb_symtab *s, upb_filedef *file, upb_status* status);
 
 /* upb_symtab_iter i;
  * for(upb_symtab_begin(&i, s, type); !upb_symtab_done(&i);
@@ -6135,9 +6588,12 @@
   return upb_symtab_lookupmsg(this, sym);
 }
 inline bool SymbolTable::Add(
-    Def*const* defs, int n, void* ref_donor, upb_status* status) {
+    Def*const* defs, size_t n, void* ref_donor, Status* status) {
   return upb_symtab_add(this, (upb_def*const*)defs, n, ref_donor, status);
 }
+inline bool SymbolTable::AddFile(FileDef* file, Status* s) {
+  return upb_symtab_addfile(this, file, s);
+}
 }  /* namespace upb */
 #endif
 
@@ -6181,14 +6637,9 @@
   /* The reader's input; this is where descriptor.proto data should be sent. */
   Sink* input();
 
-  /* Returns an array of all defs that have been parsed, and transfers ownership
-   * of them to "owner".  The number of defs is stored in *n.  Ownership of the
-   * returned array is retained and is invalidated by any other call into
-   * Reader.
-   *
-   * These defs are not frozen or resolved; they are ready to be added to a
-   * symtab. */
-  upb::Def** GetDefs(void* owner, int* n);
+  /* Use to get the FileDefs that have been parsed. */
+  size_t file_count() const;
+  FileDef* file(size_t i) const;
 
   /* Builds and returns handlers for the reader, owned by "owner." */
   static Handlers* NewHandlers(const void* owner);
@@ -6204,7 +6655,8 @@
 /* C API. */
 upb_descreader *upb_descreader_create(upb_env *e, const upb_handlers *h);
 upb_sink *upb_descreader_input(upb_descreader *r);
-upb_def **upb_descreader_getdefs(upb_descreader *r, void *owner, int *n);
+size_t upb_descreader_filecount(const upb_descreader *r);
+upb_filedef *upb_descreader_file(const upb_descreader *r, size_t i);
 const upb_handlers *upb_descreader_newhandlers(const void *owner);
 
 UPB_END_EXTERN_C
@@ -6217,8 +6669,11 @@
   return upb_descreader_create(e, h);
 }
 inline Sink* Reader::input() { return upb_descreader_input(this); }
-inline upb::Def** Reader::GetDefs(void* owner, int* n) {
-  return upb_descreader_getdefs(this, owner, n);
+inline size_t Reader::file_count() const {
+  return upb_descreader_filecount(this);
+}
+inline FileDef* Reader::file(size_t i) const {
+  return upb_descreader_file(this, i);
 }
 }  /* namespace descriptor */
 }  /* namespace upb */
@@ -6231,519 +6686,290 @@
  * actually storing protobufs.  It only contains *defs* which
  * let you reflect over a protobuf *schema*.
  */
-/* This file was generated by upbc (the upb compiler).
+/* This file was generated by upbc (the upb compiler) from the input
+ * file:
+ *
+ *     upb/descriptor/descriptor.proto
+ *
  * Do not edit -- your changes will be discarded when the file is
  * regenerated. */
 
-#ifndef GOOGLE_PROTOBUF_DESCRIPTOR_UPB_H_
-#define GOOGLE_PROTOBUF_DESCRIPTOR_UPB_H_
+#ifndef UPB_DESCRIPTOR_DESCRIPTOR_PROTO_UPB_H_
+#define UPB_DESCRIPTOR_DESCRIPTOR_PROTO_UPB_H_
 
 
-#ifdef __cplusplus
 UPB_BEGIN_EXTERN_C
-#endif
 
 /* Enums */
 
 typedef enum {
-  GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_LABEL_OPTIONAL = 1,
-  GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_LABEL_REQUIRED = 2,
-  GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_LABEL_REPEATED = 3
+  google_protobuf_FieldDescriptorProto_LABEL_OPTIONAL = 1,
+  google_protobuf_FieldDescriptorProto_LABEL_REQUIRED = 2,
+  google_protobuf_FieldDescriptorProto_LABEL_REPEATED = 3
 } google_protobuf_FieldDescriptorProto_Label;
 
 typedef enum {
-  GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_DOUBLE = 1,
-  GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_FLOAT = 2,
-  GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_INT64 = 3,
-  GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_UINT64 = 4,
-  GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_INT32 = 5,
-  GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_FIXED64 = 6,
-  GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_FIXED32 = 7,
-  GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_BOOL = 8,
-  GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_STRING = 9,
-  GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_GROUP = 10,
-  GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_MESSAGE = 11,
-  GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_BYTES = 12,
-  GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_UINT32 = 13,
-  GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_ENUM = 14,
-  GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_SFIXED32 = 15,
-  GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_SFIXED64 = 16,
-  GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_SINT32 = 17,
-  GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_SINT64 = 18
+  google_protobuf_FieldDescriptorProto_TYPE_DOUBLE = 1,
+  google_protobuf_FieldDescriptorProto_TYPE_FLOAT = 2,
+  google_protobuf_FieldDescriptorProto_TYPE_INT64 = 3,
+  google_protobuf_FieldDescriptorProto_TYPE_UINT64 = 4,
+  google_protobuf_FieldDescriptorProto_TYPE_INT32 = 5,
+  google_protobuf_FieldDescriptorProto_TYPE_FIXED64 = 6,
+  google_protobuf_FieldDescriptorProto_TYPE_FIXED32 = 7,
+  google_protobuf_FieldDescriptorProto_TYPE_BOOL = 8,
+  google_protobuf_FieldDescriptorProto_TYPE_STRING = 9,
+  google_protobuf_FieldDescriptorProto_TYPE_GROUP = 10,
+  google_protobuf_FieldDescriptorProto_TYPE_MESSAGE = 11,
+  google_protobuf_FieldDescriptorProto_TYPE_BYTES = 12,
+  google_protobuf_FieldDescriptorProto_TYPE_UINT32 = 13,
+  google_protobuf_FieldDescriptorProto_TYPE_ENUM = 14,
+  google_protobuf_FieldDescriptorProto_TYPE_SFIXED32 = 15,
+  google_protobuf_FieldDescriptorProto_TYPE_SFIXED64 = 16,
+  google_protobuf_FieldDescriptorProto_TYPE_SINT32 = 17,
+  google_protobuf_FieldDescriptorProto_TYPE_SINT64 = 18
 } google_protobuf_FieldDescriptorProto_Type;
 
 typedef enum {
-  GOOGLE_PROTOBUF_FIELDOPTIONS_STRING = 0,
-  GOOGLE_PROTOBUF_FIELDOPTIONS_CORD = 1,
-  GOOGLE_PROTOBUF_FIELDOPTIONS_STRING_PIECE = 2
+  google_protobuf_FieldOptions_STRING = 0,
+  google_protobuf_FieldOptions_CORD = 1,
+  google_protobuf_FieldOptions_STRING_PIECE = 2
 } google_protobuf_FieldOptions_CType;
 
 typedef enum {
-  GOOGLE_PROTOBUF_FILEOPTIONS_SPEED = 1,
-  GOOGLE_PROTOBUF_FILEOPTIONS_CODE_SIZE = 2,
-  GOOGLE_PROTOBUF_FILEOPTIONS_LITE_RUNTIME = 3
+  google_protobuf_FieldOptions_JS_NORMAL = 0,
+  google_protobuf_FieldOptions_JS_STRING = 1,
+  google_protobuf_FieldOptions_JS_NUMBER = 2
+} google_protobuf_FieldOptions_JSType;
+
+typedef enum {
+  google_protobuf_FileOptions_SPEED = 1,
+  google_protobuf_FileOptions_CODE_SIZE = 2,
+  google_protobuf_FileOptions_LITE_RUNTIME = 3
 } google_protobuf_FileOptions_OptimizeMode;
 
-/* Selectors */
+/* MessageDefs: call these functions to get a ref to a msgdef. */
+const upb_msgdef *upbdefs_google_protobuf_DescriptorProto_get(const void *owner);
+const upb_msgdef *upbdefs_google_protobuf_DescriptorProto_ExtensionRange_get(const void *owner);
+const upb_msgdef *upbdefs_google_protobuf_DescriptorProto_ReservedRange_get(const void *owner);
+const upb_msgdef *upbdefs_google_protobuf_EnumDescriptorProto_get(const void *owner);
+const upb_msgdef *upbdefs_google_protobuf_EnumOptions_get(const void *owner);
+const upb_msgdef *upbdefs_google_protobuf_EnumValueDescriptorProto_get(const void *owner);
+const upb_msgdef *upbdefs_google_protobuf_EnumValueOptions_get(const void *owner);
+const upb_msgdef *upbdefs_google_protobuf_FieldDescriptorProto_get(const void *owner);
+const upb_msgdef *upbdefs_google_protobuf_FieldOptions_get(const void *owner);
+const upb_msgdef *upbdefs_google_protobuf_FileDescriptorProto_get(const void *owner);
+const upb_msgdef *upbdefs_google_protobuf_FileDescriptorSet_get(const void *owner);
+const upb_msgdef *upbdefs_google_protobuf_FileOptions_get(const void *owner);
+const upb_msgdef *upbdefs_google_protobuf_MessageOptions_get(const void *owner);
+const upb_msgdef *upbdefs_google_protobuf_MethodDescriptorProto_get(const void *owner);
+const upb_msgdef *upbdefs_google_protobuf_MethodOptions_get(const void *owner);
+const upb_msgdef *upbdefs_google_protobuf_OneofDescriptorProto_get(const void *owner);
+const upb_msgdef *upbdefs_google_protobuf_ServiceDescriptorProto_get(const void *owner);
+const upb_msgdef *upbdefs_google_protobuf_ServiceOptions_get(const void *owner);
+const upb_msgdef *upbdefs_google_protobuf_SourceCodeInfo_get(const void *owner);
+const upb_msgdef *upbdefs_google_protobuf_SourceCodeInfo_Location_get(const void *owner);
+const upb_msgdef *upbdefs_google_protobuf_UninterpretedOption_get(const void *owner);
+const upb_msgdef *upbdefs_google_protobuf_UninterpretedOption_NamePart_get(const void *owner);
 
-/* google.protobuf.DescriptorProto */
-#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_FIELD_STARTSUBMSG 2
-#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NESTED_TYPE_STARTSUBMSG 3
-#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_ENUM_TYPE_STARTSUBMSG 4
-#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_RANGE_STARTSUBMSG 5
-#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_STARTSUBMSG 6
-#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_OPTIONS_STARTSUBMSG 7
-#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_FIELD_STARTSEQ 8
-#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_FIELD_ENDSEQ 9
-#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_FIELD_ENDSUBMSG 10
-#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NESTED_TYPE_STARTSEQ 11
-#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NESTED_TYPE_ENDSEQ 12
-#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NESTED_TYPE_ENDSUBMSG 13
-#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_ENUM_TYPE_STARTSEQ 14
-#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_ENUM_TYPE_ENDSEQ 15
-#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_ENUM_TYPE_ENDSUBMSG 16
-#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_RANGE_STARTSEQ 17
-#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_RANGE_ENDSEQ 18
-#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_RANGE_ENDSUBMSG 19
-#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_STARTSEQ 20
-#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_ENDSEQ 21
-#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_ENDSUBMSG 22
-#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_OPTIONS_ENDSUBMSG 23
-#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NAME_STRING 24
-#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NAME_STARTSTR 25
-#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NAME_ENDSTR 26
+/* EnumDefs: call these functions to get a ref to an enumdef. */
+const upb_enumdef *upbdefs_google_protobuf_FieldDescriptorProto_Label_get(const void *owner);
+const upb_enumdef *upbdefs_google_protobuf_FieldDescriptorProto_Type_get(const void *owner);
+const upb_enumdef *upbdefs_google_protobuf_FieldOptions_CType_get(const void *owner);
+const upb_enumdef *upbdefs_google_protobuf_FieldOptions_JSType_get(const void *owner);
+const upb_enumdef *upbdefs_google_protobuf_FileOptions_OptimizeMode_get(const void *owner);
 
-/* google.protobuf.DescriptorProto.ExtensionRange */
-#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSIONRANGE_START_INT32 2
-#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSIONRANGE_END_INT32 3
-
-/* google.protobuf.EnumDescriptorProto */
-#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_VALUE_STARTSUBMSG 2
-#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_OPTIONS_STARTSUBMSG 3
-#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_VALUE_STARTSEQ 4
-#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_VALUE_ENDSEQ 5
-#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_VALUE_ENDSUBMSG 6
-#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_OPTIONS_ENDSUBMSG 7
-#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_NAME_STRING 8
-#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_NAME_STARTSTR 9
-#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_NAME_ENDSTR 10
-
-/* google.protobuf.EnumOptions */
-#define SEL_GOOGLE_PROTOBUF_ENUMOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2
-#define SEL_GOOGLE_PROTOBUF_ENUMOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3
-#define SEL_GOOGLE_PROTOBUF_ENUMOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4
-#define SEL_GOOGLE_PROTOBUF_ENUMOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5
-#define SEL_GOOGLE_PROTOBUF_ENUMOPTIONS_ALLOW_ALIAS_BOOL 6
-
-/* google.protobuf.EnumValueDescriptorProto */
-#define SEL_GOOGLE_PROTOBUF_ENUMVALUEDESCRIPTORPROTO_OPTIONS_STARTSUBMSG 2
-#define SEL_GOOGLE_PROTOBUF_ENUMVALUEDESCRIPTORPROTO_OPTIONS_ENDSUBMSG 3
-#define SEL_GOOGLE_PROTOBUF_ENUMVALUEDESCRIPTORPROTO_NAME_STRING 4
-#define SEL_GOOGLE_PROTOBUF_ENUMVALUEDESCRIPTORPROTO_NAME_STARTSTR 5
-#define SEL_GOOGLE_PROTOBUF_ENUMVALUEDESCRIPTORPROTO_NAME_ENDSTR 6
-#define SEL_GOOGLE_PROTOBUF_ENUMVALUEDESCRIPTORPROTO_NUMBER_INT32 7
-
-/* google.protobuf.EnumValueOptions */
-#define SEL_GOOGLE_PROTOBUF_ENUMVALUEOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2
-#define SEL_GOOGLE_PROTOBUF_ENUMVALUEOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3
-#define SEL_GOOGLE_PROTOBUF_ENUMVALUEOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4
-#define SEL_GOOGLE_PROTOBUF_ENUMVALUEOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5
-
-/* google.protobuf.FieldDescriptorProto */
-#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_OPTIONS_STARTSUBMSG 2
-#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_OPTIONS_ENDSUBMSG 3
-#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_NAME_STRING 4
-#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_NAME_STARTSTR 5
-#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_NAME_ENDSTR 6
-#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_EXTENDEE_STRING 7
-#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_EXTENDEE_STARTSTR 8
-#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_EXTENDEE_ENDSTR 9
-#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_NUMBER_INT32 10
-#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_LABEL_INT32 11
-#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_INT32 12
-#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_NAME_STRING 13
-#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_NAME_STARTSTR 14
-#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_NAME_ENDSTR 15
-#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_DEFAULT_VALUE_STRING 16
-#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_DEFAULT_VALUE_STARTSTR 17
-#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_DEFAULT_VALUE_ENDSTR 18
-
-/* google.protobuf.FieldOptions */
-#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2
-#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3
-#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4
-#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5
-#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_CTYPE_INT32 6
-#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_PACKED_BOOL 7
-#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_DEPRECATED_BOOL 8
-#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_LAZY_BOOL 9
-#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_EXPERIMENTAL_MAP_KEY_STRING 10
-#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_EXPERIMENTAL_MAP_KEY_STARTSTR 11
-#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_EXPERIMENTAL_MAP_KEY_ENDSTR 12
-#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_WEAK_BOOL 13
-
-/* google.protobuf.FileDescriptorProto */
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_MESSAGE_TYPE_STARTSUBMSG 2
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_ENUM_TYPE_STARTSUBMSG 3
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_SERVICE_STARTSUBMSG 4
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_EXTENSION_STARTSUBMSG 5
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_OPTIONS_STARTSUBMSG 6
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_SOURCE_CODE_INFO_STARTSUBMSG 7
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_MESSAGE_TYPE_STARTSEQ 8
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_MESSAGE_TYPE_ENDSEQ 9
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_MESSAGE_TYPE_ENDSUBMSG 10
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_ENUM_TYPE_STARTSEQ 11
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_ENUM_TYPE_ENDSEQ 12
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_ENUM_TYPE_ENDSUBMSG 13
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_SERVICE_STARTSEQ 14
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_SERVICE_ENDSEQ 15
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_SERVICE_ENDSUBMSG 16
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_EXTENSION_STARTSEQ 17
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_EXTENSION_ENDSEQ 18
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_EXTENSION_ENDSUBMSG 19
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_OPTIONS_ENDSUBMSG 20
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_SOURCE_CODE_INFO_ENDSUBMSG 21
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_NAME_STRING 22
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_NAME_STARTSTR 23
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_NAME_ENDSTR 24
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_PACKAGE_STRING 25
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_PACKAGE_STARTSTR 26
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_PACKAGE_ENDSTR 27
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_DEPENDENCY_STARTSEQ 28
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_DEPENDENCY_ENDSEQ 29
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_DEPENDENCY_STRING 30
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_DEPENDENCY_STARTSTR 31
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_DEPENDENCY_ENDSTR 32
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_PUBLIC_DEPENDENCY_STARTSEQ 33
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_PUBLIC_DEPENDENCY_ENDSEQ 34
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_PUBLIC_DEPENDENCY_INT32 35
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_WEAK_DEPENDENCY_STARTSEQ 36
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_WEAK_DEPENDENCY_ENDSEQ 37
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_WEAK_DEPENDENCY_INT32 38
-
-/* google.protobuf.FileDescriptorSet */
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORSET_FILE_STARTSUBMSG 2
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORSET_FILE_STARTSEQ 3
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORSET_FILE_ENDSEQ 4
-#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORSET_FILE_ENDSUBMSG 5
-
-/* google.protobuf.FileOptions */
-#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2
-#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3
-#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4
-#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5
-#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_PACKAGE_STRING 6
-#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_PACKAGE_STARTSTR 7
-#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_PACKAGE_ENDSTR 8
-#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_OUTER_CLASSNAME_STRING 9
-#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_OUTER_CLASSNAME_STARTSTR 10
-#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_OUTER_CLASSNAME_ENDSTR 11
-#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_OPTIMIZE_FOR_INT32 12
-#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_MULTIPLE_FILES_BOOL 13
-#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_GO_PACKAGE_STRING 14
-#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_GO_PACKAGE_STARTSTR 15
-#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_GO_PACKAGE_ENDSTR 16
-#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_CC_GENERIC_SERVICES_BOOL 17
-#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_GENERIC_SERVICES_BOOL 18
-#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_PY_GENERIC_SERVICES_BOOL 19
-#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_GENERATE_EQUALS_AND_HASH_BOOL 20
-
-/* google.protobuf.MessageOptions */
-#define SEL_GOOGLE_PROTOBUF_MESSAGEOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2
-#define SEL_GOOGLE_PROTOBUF_MESSAGEOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3
-#define SEL_GOOGLE_PROTOBUF_MESSAGEOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4
-#define SEL_GOOGLE_PROTOBUF_MESSAGEOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5
-#define SEL_GOOGLE_PROTOBUF_MESSAGEOPTIONS_MESSAGE_SET_WIRE_FORMAT_BOOL 6
-#define SEL_GOOGLE_PROTOBUF_MESSAGEOPTIONS_NO_STANDARD_DESCRIPTOR_ACCESSOR_BOOL 7
-
-/* google.protobuf.MethodDescriptorProto */
-#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_OPTIONS_STARTSUBMSG 2
-#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_OPTIONS_ENDSUBMSG 3
-#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_NAME_STRING 4
-#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_NAME_STARTSTR 5
-#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_NAME_ENDSTR 6
-#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_INPUT_TYPE_STRING 7
-#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_INPUT_TYPE_STARTSTR 8
-#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_INPUT_TYPE_ENDSTR 9
-#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_OUTPUT_TYPE_STRING 10
-#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_OUTPUT_TYPE_STARTSTR 11
-#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_OUTPUT_TYPE_ENDSTR 12
-
-/* google.protobuf.MethodOptions */
-#define SEL_GOOGLE_PROTOBUF_METHODOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2
-#define SEL_GOOGLE_PROTOBUF_METHODOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3
-#define SEL_GOOGLE_PROTOBUF_METHODOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4
-#define SEL_GOOGLE_PROTOBUF_METHODOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5
-
-/* google.protobuf.ServiceDescriptorProto */
-#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_METHOD_STARTSUBMSG 2
-#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_OPTIONS_STARTSUBMSG 3
-#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_METHOD_STARTSEQ 4
-#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_METHOD_ENDSEQ 5
-#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_METHOD_ENDSUBMSG 6
-#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_OPTIONS_ENDSUBMSG 7
-#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_NAME_STRING 8
-#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_NAME_STARTSTR 9
-#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_NAME_ENDSTR 10
-
-/* google.protobuf.ServiceOptions */
-#define SEL_GOOGLE_PROTOBUF_SERVICEOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2
-#define SEL_GOOGLE_PROTOBUF_SERVICEOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3
-#define SEL_GOOGLE_PROTOBUF_SERVICEOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4
-#define SEL_GOOGLE_PROTOBUF_SERVICEOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5
-
-/* google.protobuf.SourceCodeInfo */
-#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_STARTSUBMSG 2
-#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_STARTSEQ 3
-#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_ENDSEQ 4
-#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_ENDSUBMSG 5
-
-/* google.protobuf.SourceCodeInfo.Location */
-#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_PATH_STARTSEQ 2
-#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_PATH_ENDSEQ 3
-#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_PATH_INT32 4
-#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_SPAN_STARTSEQ 5
-#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_SPAN_ENDSEQ 6
-#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_SPAN_INT32 7
-#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_LEADING_COMMENTS_STRING 8
-#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_LEADING_COMMENTS_STARTSTR 9
-#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_LEADING_COMMENTS_ENDSTR 10
-#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_TRAILING_COMMENTS_STRING 11
-#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_TRAILING_COMMENTS_STARTSTR 12
-#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_TRAILING_COMMENTS_ENDSTR 13
-
-/* google.protobuf.UninterpretedOption */
-#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAME_STARTSUBMSG 2
-#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAME_STARTSEQ 3
-#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAME_ENDSEQ 4
-#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAME_ENDSUBMSG 5
-#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_IDENTIFIER_VALUE_STRING 6
-#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_IDENTIFIER_VALUE_STARTSTR 7
-#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_IDENTIFIER_VALUE_ENDSTR 8
-#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_POSITIVE_INT_VALUE_UINT64 9
-#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NEGATIVE_INT_VALUE_INT64 10
-#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_DOUBLE_VALUE_DOUBLE 11
-#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_STRING_VALUE_STRING 12
-#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_STRING_VALUE_STARTSTR 13
-#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_STRING_VALUE_ENDSTR 14
-#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_AGGREGATE_VALUE_STRING 15
-#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_AGGREGATE_VALUE_STARTSTR 16
-#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_AGGREGATE_VALUE_ENDSTR 17
-
-/* google.protobuf.UninterpretedOption.NamePart */
-#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAMEPART_NAME_PART_STRING 2
-#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAMEPART_NAME_PART_STARTSTR 3
-#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAMEPART_NAME_PART_ENDSTR 4
-#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAMEPART_IS_EXTENSION_BOOL 5
-
-const upb_symtab *upbdefs_google_protobuf_descriptor(const void *owner);
-
-/* MessageDefs */
-UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_DescriptorProto(const upb_symtab *s) {
-  const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.DescriptorProto");
-  assert(m);
-  return m;
+/* Functions to test whether this message is of a certain type. */
+UPB_INLINE bool upbdefs_google_protobuf_DescriptorProto_is(const upb_msgdef *m) {
+  return strcmp(upb_msgdef_fullname(m), "google.protobuf.DescriptorProto") == 0;
 }
-UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_DescriptorProto_ExtensionRange(const upb_symtab *s) {
-  const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.DescriptorProto.ExtensionRange");
-  assert(m);
-  return m;
+UPB_INLINE bool upbdefs_google_protobuf_DescriptorProto_ExtensionRange_is(const upb_msgdef *m) {
+  return strcmp(upb_msgdef_fullname(m), "google.protobuf.DescriptorProto.ExtensionRange") == 0;
 }
-UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_EnumDescriptorProto(const upb_symtab *s) {
-  const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.EnumDescriptorProto");
-  assert(m);
-  return m;
+UPB_INLINE bool upbdefs_google_protobuf_DescriptorProto_ReservedRange_is(const upb_msgdef *m) {
+  return strcmp(upb_msgdef_fullname(m), "google.protobuf.DescriptorProto.ReservedRange") == 0;
 }
-UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_EnumOptions(const upb_symtab *s) {
-  const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.EnumOptions");
-  assert(m);
-  return m;
+UPB_INLINE bool upbdefs_google_protobuf_EnumDescriptorProto_is(const upb_msgdef *m) {
+  return strcmp(upb_msgdef_fullname(m), "google.protobuf.EnumDescriptorProto") == 0;
 }
-UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_EnumValueDescriptorProto(const upb_symtab *s) {
-  const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.EnumValueDescriptorProto");
-  assert(m);
-  return m;
+UPB_INLINE bool upbdefs_google_protobuf_EnumOptions_is(const upb_msgdef *m) {
+  return strcmp(upb_msgdef_fullname(m), "google.protobuf.EnumOptions") == 0;
 }
-UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_EnumValueOptions(const upb_symtab *s) {
-  const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.EnumValueOptions");
-  assert(m);
-  return m;
+UPB_INLINE bool upbdefs_google_protobuf_EnumValueDescriptorProto_is(const upb_msgdef *m) {
+  return strcmp(upb_msgdef_fullname(m), "google.protobuf.EnumValueDescriptorProto") == 0;
 }
-UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_FieldDescriptorProto(const upb_symtab *s) {
-  const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.FieldDescriptorProto");
-  assert(m);
-  return m;
+UPB_INLINE bool upbdefs_google_protobuf_EnumValueOptions_is(const upb_msgdef *m) {
+  return strcmp(upb_msgdef_fullname(m), "google.protobuf.EnumValueOptions") == 0;
 }
-UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_FieldOptions(const upb_symtab *s) {
-  const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.FieldOptions");
-  assert(m);
-  return m;
+UPB_INLINE bool upbdefs_google_protobuf_FieldDescriptorProto_is(const upb_msgdef *m) {
+  return strcmp(upb_msgdef_fullname(m), "google.protobuf.FieldDescriptorProto") == 0;
 }
-UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_FileDescriptorProto(const upb_symtab *s) {
-  const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.FileDescriptorProto");
-  assert(m);
-  return m;
+UPB_INLINE bool upbdefs_google_protobuf_FieldOptions_is(const upb_msgdef *m) {
+  return strcmp(upb_msgdef_fullname(m), "google.protobuf.FieldOptions") == 0;
 }
-UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_FileDescriptorSet(const upb_symtab *s) {
-  const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.FileDescriptorSet");
-  assert(m);
-  return m;
+UPB_INLINE bool upbdefs_google_protobuf_FileDescriptorProto_is(const upb_msgdef *m) {
+  return strcmp(upb_msgdef_fullname(m), "google.protobuf.FileDescriptorProto") == 0;
 }
-UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_FileOptions(const upb_symtab *s) {
-  const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.FileOptions");
-  assert(m);
-  return m;
+UPB_INLINE bool upbdefs_google_protobuf_FileDescriptorSet_is(const upb_msgdef *m) {
+  return strcmp(upb_msgdef_fullname(m), "google.protobuf.FileDescriptorSet") == 0;
 }
-UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_MessageOptions(const upb_symtab *s) {
-  const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.MessageOptions");
-  assert(m);
-  return m;
+UPB_INLINE bool upbdefs_google_protobuf_FileOptions_is(const upb_msgdef *m) {
+  return strcmp(upb_msgdef_fullname(m), "google.protobuf.FileOptions") == 0;
 }
-UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_MethodDescriptorProto(const upb_symtab *s) {
-  const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.MethodDescriptorProto");
-  assert(m);
-  return m;
+UPB_INLINE bool upbdefs_google_protobuf_MessageOptions_is(const upb_msgdef *m) {
+  return strcmp(upb_msgdef_fullname(m), "google.protobuf.MessageOptions") == 0;
 }
-UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_MethodOptions(const upb_symtab *s) {
-  const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.MethodOptions");
-  assert(m);
-  return m;
+UPB_INLINE bool upbdefs_google_protobuf_MethodDescriptorProto_is(const upb_msgdef *m) {
+  return strcmp(upb_msgdef_fullname(m), "google.protobuf.MethodDescriptorProto") == 0;
 }
-UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_ServiceDescriptorProto(const upb_symtab *s) {
-  const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.ServiceDescriptorProto");
-  assert(m);
-  return m;
+UPB_INLINE bool upbdefs_google_protobuf_MethodOptions_is(const upb_msgdef *m) {
+  return strcmp(upb_msgdef_fullname(m), "google.protobuf.MethodOptions") == 0;
 }
-UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_ServiceOptions(const upb_symtab *s) {
-  const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.ServiceOptions");
-  assert(m);
-  return m;
+UPB_INLINE bool upbdefs_google_protobuf_OneofDescriptorProto_is(const upb_msgdef *m) {
+  return strcmp(upb_msgdef_fullname(m), "google.protobuf.OneofDescriptorProto") == 0;
 }
-UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_SourceCodeInfo(const upb_symtab *s) {
-  const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.SourceCodeInfo");
-  assert(m);
-  return m;
+UPB_INLINE bool upbdefs_google_protobuf_ServiceDescriptorProto_is(const upb_msgdef *m) {
+  return strcmp(upb_msgdef_fullname(m), "google.protobuf.ServiceDescriptorProto") == 0;
 }
-UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_SourceCodeInfo_Location(const upb_symtab *s) {
-  const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.SourceCodeInfo.Location");
-  assert(m);
-  return m;
+UPB_INLINE bool upbdefs_google_protobuf_ServiceOptions_is(const upb_msgdef *m) {
+  return strcmp(upb_msgdef_fullname(m), "google.protobuf.ServiceOptions") == 0;
 }
-UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_UninterpretedOption(const upb_symtab *s) {
-  const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.UninterpretedOption");
-  assert(m);
-  return m;
+UPB_INLINE bool upbdefs_google_protobuf_SourceCodeInfo_is(const upb_msgdef *m) {
+  return strcmp(upb_msgdef_fullname(m), "google.protobuf.SourceCodeInfo") == 0;
 }
-UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_UninterpretedOption_NamePart(const upb_symtab *s) {
-  const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.UninterpretedOption.NamePart");
-  assert(m);
-  return m;
+UPB_INLINE bool upbdefs_google_protobuf_SourceCodeInfo_Location_is(const upb_msgdef *m) {
+  return strcmp(upb_msgdef_fullname(m), "google.protobuf.SourceCodeInfo.Location") == 0;
+}
+UPB_INLINE bool upbdefs_google_protobuf_UninterpretedOption_is(const upb_msgdef *m) {
+  return strcmp(upb_msgdef_fullname(m), "google.protobuf.UninterpretedOption") == 0;
+}
+UPB_INLINE bool upbdefs_google_protobuf_UninterpretedOption_NamePart_is(const upb_msgdef *m) {
+  return strcmp(upb_msgdef_fullname(m), "google.protobuf.UninterpretedOption.NamePart") == 0;
+}
+
+/* Functions to test whether this enum is of a certain type. */
+UPB_INLINE bool upbdefs_google_protobuf_FieldDescriptorProto_Label_is(const upb_enumdef *e) {
+  return strcmp(upb_enumdef_fullname(e), "google.protobuf.FieldDescriptorProto.Label") == 0;
+}
+UPB_INLINE bool upbdefs_google_protobuf_FieldDescriptorProto_Type_is(const upb_enumdef *e) {
+  return strcmp(upb_enumdef_fullname(e), "google.protobuf.FieldDescriptorProto.Type") == 0;
+}
+UPB_INLINE bool upbdefs_google_protobuf_FieldOptions_CType_is(const upb_enumdef *e) {
+  return strcmp(upb_enumdef_fullname(e), "google.protobuf.FieldOptions.CType") == 0;
+}
+UPB_INLINE bool upbdefs_google_protobuf_FieldOptions_JSType_is(const upb_enumdef *e) {
+  return strcmp(upb_enumdef_fullname(e), "google.protobuf.FieldOptions.JSType") == 0;
+}
+UPB_INLINE bool upbdefs_google_protobuf_FileOptions_OptimizeMode_is(const upb_enumdef *e) {
+  return strcmp(upb_enumdef_fullname(e), "google.protobuf.FileOptions.OptimizeMode") == 0;
 }
 
 
-/* EnumDefs */
-UPB_INLINE const upb_enumdef *upbdefs_google_protobuf_FieldDescriptorProto_Label(const upb_symtab *s) {
-  const upb_enumdef *e = upb_symtab_lookupenum(s, "google.protobuf.FieldDescriptorProto.Label");
-  assert(e);
-  return e;
-}
-UPB_INLINE const upb_enumdef *upbdefs_google_protobuf_FieldDescriptorProto_Type(const upb_symtab *s) {
-  const upb_enumdef *e = upb_symtab_lookupenum(s, "google.protobuf.FieldDescriptorProto.Type");
-  assert(e);
-  return e;
-}
-UPB_INLINE const upb_enumdef *upbdefs_google_protobuf_FieldOptions_CType(const upb_symtab *s) {
-  const upb_enumdef *e = upb_symtab_lookupenum(s, "google.protobuf.FieldOptions.CType");
-  assert(e);
-  return e;
-}
-UPB_INLINE const upb_enumdef *upbdefs_google_protobuf_FileOptions_OptimizeMode(const upb_symtab *s) {
-  const upb_enumdef *e = upb_symtab_lookupenum(s, "google.protobuf.FileOptions.OptimizeMode");
-  assert(e);
-  return e;
-}
-
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_ExtensionRange_end(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto_ExtensionRange(s), 2); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_ExtensionRange_start(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto_ExtensionRange(s), 1); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_enum_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 4); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_extension(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 6); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_extension_range(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 5); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_field(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 2); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 1); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_nested_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 3); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 7); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumDescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumDescriptorProto(s), 1); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumDescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumDescriptorProto(s), 3); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumDescriptorProto_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumDescriptorProto(s), 2); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumOptions_allow_alias(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumOptions(s), 2); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumOptions(s), 999); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumValueDescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumValueDescriptorProto(s), 1); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumValueDescriptorProto_number(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumValueDescriptorProto(s), 2); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumValueDescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumValueDescriptorProto(s), 3); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumValueOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumValueOptions(s), 999); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_default_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 7); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_extendee(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 2); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_label(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 4); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 1); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_number(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 3); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 8); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 5); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_type_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 6); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_ctype(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 1); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_deprecated(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 3); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_experimental_map_key(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 9); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_lazy(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 5); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_packed(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 2); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 999); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_weak(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 10); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_dependency(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 3); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_enum_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 5); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_extension(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 7); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_message_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 4); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 1); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 8); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_package(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 2); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_public_dependency(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 10); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_service(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 6); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_source_code_info(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 9); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_weak_dependency(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 11); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorSet_file(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorSet(s), 1); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_cc_generic_services(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 16); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_go_package(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 11); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_java_generate_equals_and_hash(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 20); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_java_generic_services(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 17); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_java_multiple_files(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 10); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_java_outer_classname(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 8); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_java_package(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 1); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_optimize_for(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 9); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_py_generic_services(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 18); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 999); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MessageOptions_message_set_wire_format(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MessageOptions(s), 1); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MessageOptions_no_standard_descriptor_accessor(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MessageOptions(s), 2); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MessageOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MessageOptions(s), 999); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_input_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MethodDescriptorProto(s), 2); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MethodDescriptorProto(s), 1); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MethodDescriptorProto(s), 4); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_output_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MethodDescriptorProto(s), 3); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MethodOptions(s), 999); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_ServiceDescriptorProto_method(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_ServiceDescriptorProto(s), 2); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_ServiceDescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_ServiceDescriptorProto(s), 1); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_ServiceDescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_ServiceDescriptorProto(s), 3); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_ServiceOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_ServiceOptions(s), 999); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_Location_leading_comments(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_SourceCodeInfo_Location(s), 3); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_Location_path(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_SourceCodeInfo_Location(s), 1); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_Location_span(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_SourceCodeInfo_Location(s), 2); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_Location_trailing_comments(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_SourceCodeInfo_Location(s), 4); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_location(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_SourceCodeInfo(s), 1); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_NamePart_is_extension(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption_NamePart(s), 2); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_NamePart_name_part(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption_NamePart(s), 1); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_aggregate_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 8); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_double_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 6); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_identifier_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 3); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 2); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_negative_int_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 5); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_positive_int_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 4); }
-UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_string_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 7); }
+/* Functions to get a fielddef from a msgdef reference. */
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_ExtensionRange_f_end(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_DescriptorProto_ExtensionRange_is(m)); return upb_msgdef_itof(m, 2); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_ExtensionRange_f_start(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_DescriptorProto_ExtensionRange_is(m)); return upb_msgdef_itof(m, 1); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_ReservedRange_f_end(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_DescriptorProto_ReservedRange_is(m)); return upb_msgdef_itof(m, 2); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_ReservedRange_f_start(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_DescriptorProto_ReservedRange_is(m)); return upb_msgdef_itof(m, 1); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_f_enum_type(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_DescriptorProto_is(m)); return upb_msgdef_itof(m, 4); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_f_extension(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_DescriptorProto_is(m)); return upb_msgdef_itof(m, 6); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_f_extension_range(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_DescriptorProto_is(m)); return upb_msgdef_itof(m, 5); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_f_field(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_DescriptorProto_is(m)); return upb_msgdef_itof(m, 2); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_f_name(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_DescriptorProto_is(m)); return upb_msgdef_itof(m, 1); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_f_nested_type(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_DescriptorProto_is(m)); return upb_msgdef_itof(m, 3); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_f_oneof_decl(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_DescriptorProto_is(m)); return upb_msgdef_itof(m, 8); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_f_options(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_DescriptorProto_is(m)); return upb_msgdef_itof(m, 7); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_f_reserved_name(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_DescriptorProto_is(m)); return upb_msgdef_itof(m, 10); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_f_reserved_range(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_DescriptorProto_is(m)); return upb_msgdef_itof(m, 9); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumDescriptorProto_f_name(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_EnumDescriptorProto_is(m)); return upb_msgdef_itof(m, 1); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumDescriptorProto_f_options(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_EnumDescriptorProto_is(m)); return upb_msgdef_itof(m, 3); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumDescriptorProto_f_value(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_EnumDescriptorProto_is(m)); return upb_msgdef_itof(m, 2); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumOptions_f_allow_alias(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_EnumOptions_is(m)); return upb_msgdef_itof(m, 2); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumOptions_f_deprecated(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_EnumOptions_is(m)); return upb_msgdef_itof(m, 3); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumOptions_f_uninterpreted_option(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_EnumOptions_is(m)); return upb_msgdef_itof(m, 999); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumValueDescriptorProto_f_name(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_EnumValueDescriptorProto_is(m)); return upb_msgdef_itof(m, 1); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumValueDescriptorProto_f_number(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_EnumValueDescriptorProto_is(m)); return upb_msgdef_itof(m, 2); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumValueDescriptorProto_f_options(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_EnumValueDescriptorProto_is(m)); return upb_msgdef_itof(m, 3); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumValueOptions_f_deprecated(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_EnumValueOptions_is(m)); return upb_msgdef_itof(m, 1); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumValueOptions_f_uninterpreted_option(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_EnumValueOptions_is(m)); return upb_msgdef_itof(m, 999); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_f_default_value(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FieldDescriptorProto_is(m)); return upb_msgdef_itof(m, 7); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_f_extendee(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FieldDescriptorProto_is(m)); return upb_msgdef_itof(m, 2); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_f_json_name(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FieldDescriptorProto_is(m)); return upb_msgdef_itof(m, 10); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_f_label(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FieldDescriptorProto_is(m)); return upb_msgdef_itof(m, 4); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_f_name(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FieldDescriptorProto_is(m)); return upb_msgdef_itof(m, 1); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_f_number(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FieldDescriptorProto_is(m)); return upb_msgdef_itof(m, 3); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_f_oneof_index(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FieldDescriptorProto_is(m)); return upb_msgdef_itof(m, 9); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_f_options(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FieldDescriptorProto_is(m)); return upb_msgdef_itof(m, 8); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_f_type(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FieldDescriptorProto_is(m)); return upb_msgdef_itof(m, 5); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_f_type_name(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FieldDescriptorProto_is(m)); return upb_msgdef_itof(m, 6); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_f_ctype(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FieldOptions_is(m)); return upb_msgdef_itof(m, 1); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_f_deprecated(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FieldOptions_is(m)); return upb_msgdef_itof(m, 3); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_f_jstype(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FieldOptions_is(m)); return upb_msgdef_itof(m, 6); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_f_lazy(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FieldOptions_is(m)); return upb_msgdef_itof(m, 5); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_f_packed(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FieldOptions_is(m)); return upb_msgdef_itof(m, 2); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_f_uninterpreted_option(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FieldOptions_is(m)); return upb_msgdef_itof(m, 999); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_f_weak(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FieldOptions_is(m)); return upb_msgdef_itof(m, 10); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_f_dependency(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FileDescriptorProto_is(m)); return upb_msgdef_itof(m, 3); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_f_enum_type(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FileDescriptorProto_is(m)); return upb_msgdef_itof(m, 5); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_f_extension(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FileDescriptorProto_is(m)); return upb_msgdef_itof(m, 7); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_f_message_type(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FileDescriptorProto_is(m)); return upb_msgdef_itof(m, 4); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_f_name(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FileDescriptorProto_is(m)); return upb_msgdef_itof(m, 1); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_f_options(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FileDescriptorProto_is(m)); return upb_msgdef_itof(m, 8); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_f_package(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FileDescriptorProto_is(m)); return upb_msgdef_itof(m, 2); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_f_public_dependency(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FileDescriptorProto_is(m)); return upb_msgdef_itof(m, 10); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_f_service(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FileDescriptorProto_is(m)); return upb_msgdef_itof(m, 6); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_f_source_code_info(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FileDescriptorProto_is(m)); return upb_msgdef_itof(m, 9); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_f_syntax(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FileDescriptorProto_is(m)); return upb_msgdef_itof(m, 12); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_f_weak_dependency(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FileDescriptorProto_is(m)); return upb_msgdef_itof(m, 11); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorSet_f_file(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FileDescriptorSet_is(m)); return upb_msgdef_itof(m, 1); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_f_cc_enable_arenas(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FileOptions_is(m)); return upb_msgdef_itof(m, 31); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_f_cc_generic_services(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FileOptions_is(m)); return upb_msgdef_itof(m, 16); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_f_csharp_namespace(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FileOptions_is(m)); return upb_msgdef_itof(m, 37); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_f_deprecated(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FileOptions_is(m)); return upb_msgdef_itof(m, 23); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_f_go_package(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FileOptions_is(m)); return upb_msgdef_itof(m, 11); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_f_java_generate_equals_and_hash(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FileOptions_is(m)); return upb_msgdef_itof(m, 20); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_f_java_generic_services(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FileOptions_is(m)); return upb_msgdef_itof(m, 17); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_f_java_multiple_files(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FileOptions_is(m)); return upb_msgdef_itof(m, 10); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_f_java_outer_classname(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FileOptions_is(m)); return upb_msgdef_itof(m, 8); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_f_java_package(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FileOptions_is(m)); return upb_msgdef_itof(m, 1); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_f_java_string_check_utf8(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FileOptions_is(m)); return upb_msgdef_itof(m, 27); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_f_javanano_use_deprecated_package(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FileOptions_is(m)); return upb_msgdef_itof(m, 38); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_f_objc_class_prefix(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FileOptions_is(m)); return upb_msgdef_itof(m, 36); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_f_optimize_for(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FileOptions_is(m)); return upb_msgdef_itof(m, 9); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_f_py_generic_services(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FileOptions_is(m)); return upb_msgdef_itof(m, 18); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_f_uninterpreted_option(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_FileOptions_is(m)); return upb_msgdef_itof(m, 999); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MessageOptions_f_deprecated(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_MessageOptions_is(m)); return upb_msgdef_itof(m, 3); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MessageOptions_f_map_entry(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_MessageOptions_is(m)); return upb_msgdef_itof(m, 7); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MessageOptions_f_message_set_wire_format(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_MessageOptions_is(m)); return upb_msgdef_itof(m, 1); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MessageOptions_f_no_standard_descriptor_accessor(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_MessageOptions_is(m)); return upb_msgdef_itof(m, 2); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MessageOptions_f_uninterpreted_option(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_MessageOptions_is(m)); return upb_msgdef_itof(m, 999); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_f_client_streaming(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_MethodDescriptorProto_is(m)); return upb_msgdef_itof(m, 5); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_f_input_type(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_MethodDescriptorProto_is(m)); return upb_msgdef_itof(m, 2); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_f_name(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_MethodDescriptorProto_is(m)); return upb_msgdef_itof(m, 1); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_f_options(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_MethodDescriptorProto_is(m)); return upb_msgdef_itof(m, 4); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_f_output_type(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_MethodDescriptorProto_is(m)); return upb_msgdef_itof(m, 3); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_f_server_streaming(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_MethodDescriptorProto_is(m)); return upb_msgdef_itof(m, 6); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodOptions_f_deprecated(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_MethodOptions_is(m)); return upb_msgdef_itof(m, 33); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodOptions_f_uninterpreted_option(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_MethodOptions_is(m)); return upb_msgdef_itof(m, 999); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_OneofDescriptorProto_f_name(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_OneofDescriptorProto_is(m)); return upb_msgdef_itof(m, 1); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_ServiceDescriptorProto_f_method(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_ServiceDescriptorProto_is(m)); return upb_msgdef_itof(m, 2); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_ServiceDescriptorProto_f_name(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_ServiceDescriptorProto_is(m)); return upb_msgdef_itof(m, 1); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_ServiceDescriptorProto_f_options(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_ServiceDescriptorProto_is(m)); return upb_msgdef_itof(m, 3); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_ServiceOptions_f_deprecated(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_ServiceOptions_is(m)); return upb_msgdef_itof(m, 33); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_ServiceOptions_f_uninterpreted_option(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_ServiceOptions_is(m)); return upb_msgdef_itof(m, 999); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_Location_f_leading_comments(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_SourceCodeInfo_Location_is(m)); return upb_msgdef_itof(m, 3); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_Location_f_leading_detached_comments(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_SourceCodeInfo_Location_is(m)); return upb_msgdef_itof(m, 6); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_Location_f_path(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_SourceCodeInfo_Location_is(m)); return upb_msgdef_itof(m, 1); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_Location_f_span(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_SourceCodeInfo_Location_is(m)); return upb_msgdef_itof(m, 2); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_Location_f_trailing_comments(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_SourceCodeInfo_Location_is(m)); return upb_msgdef_itof(m, 4); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_f_location(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_SourceCodeInfo_is(m)); return upb_msgdef_itof(m, 1); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_NamePart_f_is_extension(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_UninterpretedOption_NamePart_is(m)); return upb_msgdef_itof(m, 2); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_NamePart_f_name_part(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_UninterpretedOption_NamePart_is(m)); return upb_msgdef_itof(m, 1); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_f_aggregate_value(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_UninterpretedOption_is(m)); return upb_msgdef_itof(m, 8); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_f_double_value(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_UninterpretedOption_is(m)); return upb_msgdef_itof(m, 6); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_f_identifier_value(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_UninterpretedOption_is(m)); return upb_msgdef_itof(m, 3); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_f_name(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_UninterpretedOption_is(m)); return upb_msgdef_itof(m, 2); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_f_negative_int_value(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_UninterpretedOption_is(m)); return upb_msgdef_itof(m, 5); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_f_positive_int_value(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_UninterpretedOption_is(m)); return upb_msgdef_itof(m, 4); }
+UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_f_string_value(const upb_msgdef *m) { UPB_ASSERT(upbdefs_google_protobuf_UninterpretedOption_is(m)); return upb_msgdef_itof(m, 7); }
 
 UPB_END_EXTERN_C
 
@@ -6752,277 +6978,360 @@
 namespace upbdefs {
 namespace google {
 namespace protobuf {
-namespace descriptor {
-inline upb::reffed_ptr<const upb::SymbolTable> SymbolTable() {
-  const upb::SymbolTable* s = upbdefs_google_protobuf_descriptor(&s);
-  return upb::reffed_ptr<const upb::SymbolTable>(s, &s);
-}
-}  /* namespace descriptor */
+
+class DescriptorProto : public ::upb::reffed_ptr<const ::upb::MessageDef> {
+ public:
+  DescriptorProto(const ::upb::MessageDef* m, const void *ref_donor = NULL)
+      : reffed_ptr(m, ref_donor) {
+    UPB_ASSERT(upbdefs_google_protobuf_DescriptorProto_is(m));
+  }
+
+  static DescriptorProto get() {
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_DescriptorProto_get(&m);
+    return DescriptorProto(m, &m);
+  }
+
+  class ExtensionRange : public ::upb::reffed_ptr<const ::upb::MessageDef> {
+   public:
+    ExtensionRange(const ::upb::MessageDef* m, const void *ref_donor = NULL)
+        : reffed_ptr(m, ref_donor) {
+      UPB_ASSERT(upbdefs_google_protobuf_DescriptorProto_ExtensionRange_is(m));
+    }
+
+    static ExtensionRange get() {
+      const ::upb::MessageDef* m = upbdefs_google_protobuf_DescriptorProto_ExtensionRange_get(&m);
+      return ExtensionRange(m, &m);
+    }
+  };
+
+  class ReservedRange : public ::upb::reffed_ptr<const ::upb::MessageDef> {
+   public:
+    ReservedRange(const ::upb::MessageDef* m, const void *ref_donor = NULL)
+        : reffed_ptr(m, ref_donor) {
+      UPB_ASSERT(upbdefs_google_protobuf_DescriptorProto_ReservedRange_is(m));
+    }
+
+    static ReservedRange get() {
+      const ::upb::MessageDef* m = upbdefs_google_protobuf_DescriptorProto_ReservedRange_get(&m);
+      return ReservedRange(m, &m);
+    }
+  };
+};
+
+class EnumDescriptorProto : public ::upb::reffed_ptr<const ::upb::MessageDef> {
+ public:
+  EnumDescriptorProto(const ::upb::MessageDef* m, const void *ref_donor = NULL)
+      : reffed_ptr(m, ref_donor) {
+    UPB_ASSERT(upbdefs_google_protobuf_EnumDescriptorProto_is(m));
+  }
+
+  static EnumDescriptorProto get() {
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_EnumDescriptorProto_get(&m);
+    return EnumDescriptorProto(m, &m);
+  }
+};
+
+class EnumOptions : public ::upb::reffed_ptr<const ::upb::MessageDef> {
+ public:
+  EnumOptions(const ::upb::MessageDef* m, const void *ref_donor = NULL)
+      : reffed_ptr(m, ref_donor) {
+    UPB_ASSERT(upbdefs_google_protobuf_EnumOptions_is(m));
+  }
+
+  static EnumOptions get() {
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_EnumOptions_get(&m);
+    return EnumOptions(m, &m);
+  }
+};
+
+class EnumValueDescriptorProto : public ::upb::reffed_ptr<const ::upb::MessageDef> {
+ public:
+  EnumValueDescriptorProto(const ::upb::MessageDef* m, const void *ref_donor = NULL)
+      : reffed_ptr(m, ref_donor) {
+    UPB_ASSERT(upbdefs_google_protobuf_EnumValueDescriptorProto_is(m));
+  }
+
+  static EnumValueDescriptorProto get() {
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_EnumValueDescriptorProto_get(&m);
+    return EnumValueDescriptorProto(m, &m);
+  }
+};
+
+class EnumValueOptions : public ::upb::reffed_ptr<const ::upb::MessageDef> {
+ public:
+  EnumValueOptions(const ::upb::MessageDef* m, const void *ref_donor = NULL)
+      : reffed_ptr(m, ref_donor) {
+    UPB_ASSERT(upbdefs_google_protobuf_EnumValueOptions_is(m));
+  }
+
+  static EnumValueOptions get() {
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_EnumValueOptions_get(&m);
+    return EnumValueOptions(m, &m);
+  }
+};
+
+class FieldDescriptorProto : public ::upb::reffed_ptr<const ::upb::MessageDef> {
+ public:
+  FieldDescriptorProto(const ::upb::MessageDef* m, const void *ref_donor = NULL)
+      : reffed_ptr(m, ref_donor) {
+    UPB_ASSERT(upbdefs_google_protobuf_FieldDescriptorProto_is(m));
+  }
+
+  static FieldDescriptorProto get() {
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_FieldDescriptorProto_get(&m);
+    return FieldDescriptorProto(m, &m);
+  }
+
+  class Label : public ::upb::reffed_ptr<const ::upb::EnumDef> {
+   public:
+    Label(const ::upb::EnumDef* e, const void *ref_donor = NULL)
+        : reffed_ptr(e, ref_donor) {
+      UPB_ASSERT(upbdefs_google_protobuf_FieldDescriptorProto_Label_is(e));
+    }
+    static Label get() {
+      const ::upb::EnumDef* e = upbdefs_google_protobuf_FieldDescriptorProto_Label_get(&e);
+      return Label(e, &e);
+    }
+  };
+
+  class Type : public ::upb::reffed_ptr<const ::upb::EnumDef> {
+   public:
+    Type(const ::upb::EnumDef* e, const void *ref_donor = NULL)
+        : reffed_ptr(e, ref_donor) {
+      UPB_ASSERT(upbdefs_google_protobuf_FieldDescriptorProto_Type_is(e));
+    }
+    static Type get() {
+      const ::upb::EnumDef* e = upbdefs_google_protobuf_FieldDescriptorProto_Type_get(&e);
+      return Type(e, &e);
+    }
+  };
+};
+
+class FieldOptions : public ::upb::reffed_ptr<const ::upb::MessageDef> {
+ public:
+  FieldOptions(const ::upb::MessageDef* m, const void *ref_donor = NULL)
+      : reffed_ptr(m, ref_donor) {
+    UPB_ASSERT(upbdefs_google_protobuf_FieldOptions_is(m));
+  }
+
+  static FieldOptions get() {
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_FieldOptions_get(&m);
+    return FieldOptions(m, &m);
+  }
+
+  class CType : public ::upb::reffed_ptr<const ::upb::EnumDef> {
+   public:
+    CType(const ::upb::EnumDef* e, const void *ref_donor = NULL)
+        : reffed_ptr(e, ref_donor) {
+      UPB_ASSERT(upbdefs_google_protobuf_FieldOptions_CType_is(e));
+    }
+    static CType get() {
+      const ::upb::EnumDef* e = upbdefs_google_protobuf_FieldOptions_CType_get(&e);
+      return CType(e, &e);
+    }
+  };
+
+  class JSType : public ::upb::reffed_ptr<const ::upb::EnumDef> {
+   public:
+    JSType(const ::upb::EnumDef* e, const void *ref_donor = NULL)
+        : reffed_ptr(e, ref_donor) {
+      UPB_ASSERT(upbdefs_google_protobuf_FieldOptions_JSType_is(e));
+    }
+    static JSType get() {
+      const ::upb::EnumDef* e = upbdefs_google_protobuf_FieldOptions_JSType_get(&e);
+      return JSType(e, &e);
+    }
+  };
+};
+
+class FileDescriptorProto : public ::upb::reffed_ptr<const ::upb::MessageDef> {
+ public:
+  FileDescriptorProto(const ::upb::MessageDef* m, const void *ref_donor = NULL)
+      : reffed_ptr(m, ref_donor) {
+    UPB_ASSERT(upbdefs_google_protobuf_FileDescriptorProto_is(m));
+  }
+
+  static FileDescriptorProto get() {
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_FileDescriptorProto_get(&m);
+    return FileDescriptorProto(m, &m);
+  }
+};
+
+class FileDescriptorSet : public ::upb::reffed_ptr<const ::upb::MessageDef> {
+ public:
+  FileDescriptorSet(const ::upb::MessageDef* m, const void *ref_donor = NULL)
+      : reffed_ptr(m, ref_donor) {
+    UPB_ASSERT(upbdefs_google_protobuf_FileDescriptorSet_is(m));
+  }
+
+  static FileDescriptorSet get() {
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_FileDescriptorSet_get(&m);
+    return FileDescriptorSet(m, &m);
+  }
+};
+
+class FileOptions : public ::upb::reffed_ptr<const ::upb::MessageDef> {
+ public:
+  FileOptions(const ::upb::MessageDef* m, const void *ref_donor = NULL)
+      : reffed_ptr(m, ref_donor) {
+    UPB_ASSERT(upbdefs_google_protobuf_FileOptions_is(m));
+  }
+
+  static FileOptions get() {
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_FileOptions_get(&m);
+    return FileOptions(m, &m);
+  }
+
+  class OptimizeMode : public ::upb::reffed_ptr<const ::upb::EnumDef> {
+   public:
+    OptimizeMode(const ::upb::EnumDef* e, const void *ref_donor = NULL)
+        : reffed_ptr(e, ref_donor) {
+      UPB_ASSERT(upbdefs_google_protobuf_FileOptions_OptimizeMode_is(e));
+    }
+    static OptimizeMode get() {
+      const ::upb::EnumDef* e = upbdefs_google_protobuf_FileOptions_OptimizeMode_get(&e);
+      return OptimizeMode(e, &e);
+    }
+  };
+};
+
+class MessageOptions : public ::upb::reffed_ptr<const ::upb::MessageDef> {
+ public:
+  MessageOptions(const ::upb::MessageDef* m, const void *ref_donor = NULL)
+      : reffed_ptr(m, ref_donor) {
+    UPB_ASSERT(upbdefs_google_protobuf_MessageOptions_is(m));
+  }
+
+  static MessageOptions get() {
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_MessageOptions_get(&m);
+    return MessageOptions(m, &m);
+  }
+};
+
+class MethodDescriptorProto : public ::upb::reffed_ptr<const ::upb::MessageDef> {
+ public:
+  MethodDescriptorProto(const ::upb::MessageDef* m, const void *ref_donor = NULL)
+      : reffed_ptr(m, ref_donor) {
+    UPB_ASSERT(upbdefs_google_protobuf_MethodDescriptorProto_is(m));
+  }
+
+  static MethodDescriptorProto get() {
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_MethodDescriptorProto_get(&m);
+    return MethodDescriptorProto(m, &m);
+  }
+};
+
+class MethodOptions : public ::upb::reffed_ptr<const ::upb::MessageDef> {
+ public:
+  MethodOptions(const ::upb::MessageDef* m, const void *ref_donor = NULL)
+      : reffed_ptr(m, ref_donor) {
+    UPB_ASSERT(upbdefs_google_protobuf_MethodOptions_is(m));
+  }
+
+  static MethodOptions get() {
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_MethodOptions_get(&m);
+    return MethodOptions(m, &m);
+  }
+};
+
+class OneofDescriptorProto : public ::upb::reffed_ptr<const ::upb::MessageDef> {
+ public:
+  OneofDescriptorProto(const ::upb::MessageDef* m, const void *ref_donor = NULL)
+      : reffed_ptr(m, ref_donor) {
+    UPB_ASSERT(upbdefs_google_protobuf_OneofDescriptorProto_is(m));
+  }
+
+  static OneofDescriptorProto get() {
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_OneofDescriptorProto_get(&m);
+    return OneofDescriptorProto(m, &m);
+  }
+};
+
+class ServiceDescriptorProto : public ::upb::reffed_ptr<const ::upb::MessageDef> {
+ public:
+  ServiceDescriptorProto(const ::upb::MessageDef* m, const void *ref_donor = NULL)
+      : reffed_ptr(m, ref_donor) {
+    UPB_ASSERT(upbdefs_google_protobuf_ServiceDescriptorProto_is(m));
+  }
+
+  static ServiceDescriptorProto get() {
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_ServiceDescriptorProto_get(&m);
+    return ServiceDescriptorProto(m, &m);
+  }
+};
+
+class ServiceOptions : public ::upb::reffed_ptr<const ::upb::MessageDef> {
+ public:
+  ServiceOptions(const ::upb::MessageDef* m, const void *ref_donor = NULL)
+      : reffed_ptr(m, ref_donor) {
+    UPB_ASSERT(upbdefs_google_protobuf_ServiceOptions_is(m));
+  }
+
+  static ServiceOptions get() {
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_ServiceOptions_get(&m);
+    return ServiceOptions(m, &m);
+  }
+};
+
+class SourceCodeInfo : public ::upb::reffed_ptr<const ::upb::MessageDef> {
+ public:
+  SourceCodeInfo(const ::upb::MessageDef* m, const void *ref_donor = NULL)
+      : reffed_ptr(m, ref_donor) {
+    UPB_ASSERT(upbdefs_google_protobuf_SourceCodeInfo_is(m));
+  }
+
+  static SourceCodeInfo get() {
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_SourceCodeInfo_get(&m);
+    return SourceCodeInfo(m, &m);
+  }
+
+  class Location : public ::upb::reffed_ptr<const ::upb::MessageDef> {
+   public:
+    Location(const ::upb::MessageDef* m, const void *ref_donor = NULL)
+        : reffed_ptr(m, ref_donor) {
+      UPB_ASSERT(upbdefs_google_protobuf_SourceCodeInfo_Location_is(m));
+    }
+
+    static Location get() {
+      const ::upb::MessageDef* m = upbdefs_google_protobuf_SourceCodeInfo_Location_get(&m);
+      return Location(m, &m);
+    }
+  };
+};
+
+class UninterpretedOption : public ::upb::reffed_ptr<const ::upb::MessageDef> {
+ public:
+  UninterpretedOption(const ::upb::MessageDef* m, const void *ref_donor = NULL)
+      : reffed_ptr(m, ref_donor) {
+    UPB_ASSERT(upbdefs_google_protobuf_UninterpretedOption_is(m));
+  }
+
+  static UninterpretedOption get() {
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_UninterpretedOption_get(&m);
+    return UninterpretedOption(m, &m);
+  }
+
+  class NamePart : public ::upb::reffed_ptr<const ::upb::MessageDef> {
+   public:
+    NamePart(const ::upb::MessageDef* m, const void *ref_donor = NULL)
+        : reffed_ptr(m, ref_donor) {
+      UPB_ASSERT(upbdefs_google_protobuf_UninterpretedOption_NamePart_is(m));
+    }
+
+    static NamePart get() {
+      const ::upb::MessageDef* m = upbdefs_google_protobuf_UninterpretedOption_NamePart_get(&m);
+      return NamePart(m, &m);
+    }
+  };
+};
+
 }  /* namespace protobuf */
 }  /* namespace google */
-
-#define RETURN_REFFED(type, func) \
-    const type* obj = func(upbdefs::google::protobuf::descriptor::SymbolTable().get()); \
-    return upb::reffed_ptr<const type>(obj);
-
-namespace google {
-namespace protobuf {
-namespace DescriptorProto {
-inline upb::reffed_ptr<const upb::MessageDef> MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_DescriptorProto) }
-inline upb::reffed_ptr<const upb::FieldDef> enum_type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_enum_type) }
-inline upb::reffed_ptr<const upb::FieldDef> extension() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_extension) }
-inline upb::reffed_ptr<const upb::FieldDef> extension_range() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_extension_range) }
-inline upb::reffed_ptr<const upb::FieldDef> field() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_field) }
-inline upb::reffed_ptr<const upb::FieldDef> name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_name) }
-inline upb::reffed_ptr<const upb::FieldDef> nested_type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_nested_type) }
-inline upb::reffed_ptr<const upb::FieldDef> options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_options) }
-}  /* namespace DescriptorProto */
-}  /* namespace protobuf */
-}  /* namespace google */
-
-namespace google {
-namespace protobuf {
-namespace DescriptorProto {
-namespace ExtensionRange {
-inline upb::reffed_ptr<const upb::MessageDef> MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_DescriptorProto_ExtensionRange) }
-inline upb::reffed_ptr<const upb::FieldDef> end() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_ExtensionRange_end) }
-inline upb::reffed_ptr<const upb::FieldDef> start() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_ExtensionRange_start) }
-}  /* namespace ExtensionRange */
-}  /* namespace DescriptorProto */
-}  /* namespace protobuf */
-}  /* namespace google */
-
-namespace google {
-namespace protobuf {
-namespace EnumDescriptorProto {
-inline upb::reffed_ptr<const upb::MessageDef> MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_EnumDescriptorProto) }
-inline upb::reffed_ptr<const upb::FieldDef> name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumDescriptorProto_name) }
-inline upb::reffed_ptr<const upb::FieldDef> options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumDescriptorProto_options) }
-inline upb::reffed_ptr<const upb::FieldDef> value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumDescriptorProto_value) }
-}  /* namespace EnumDescriptorProto */
-}  /* namespace protobuf */
-}  /* namespace google */
-
-namespace google {
-namespace protobuf {
-namespace EnumOptions {
-inline upb::reffed_ptr<const upb::MessageDef> MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_EnumOptions) }
-inline upb::reffed_ptr<const upb::FieldDef> allow_alias() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumOptions_allow_alias) }
-inline upb::reffed_ptr<const upb::FieldDef> uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumOptions_uninterpreted_option) }
-}  /* namespace EnumOptions */
-}  /* namespace protobuf */
-}  /* namespace google */
-
-namespace google {
-namespace protobuf {
-namespace EnumValueDescriptorProto {
-inline upb::reffed_ptr<const upb::MessageDef> MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_EnumValueDescriptorProto) }
-inline upb::reffed_ptr<const upb::FieldDef> name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumValueDescriptorProto_name) }
-inline upb::reffed_ptr<const upb::FieldDef> number() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumValueDescriptorProto_number) }
-inline upb::reffed_ptr<const upb::FieldDef> options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumValueDescriptorProto_options) }
-}  /* namespace EnumValueDescriptorProto */
-}  /* namespace protobuf */
-}  /* namespace google */
-
-namespace google {
-namespace protobuf {
-namespace EnumValueOptions {
-inline upb::reffed_ptr<const upb::MessageDef> MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_EnumValueOptions) }
-inline upb::reffed_ptr<const upb::FieldDef> uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumValueOptions_uninterpreted_option) }
-}  /* namespace EnumValueOptions */
-}  /* namespace protobuf */
-}  /* namespace google */
-
-namespace google {
-namespace protobuf {
-namespace FieldDescriptorProto {
-inline upb::reffed_ptr<const upb::MessageDef> MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_FieldDescriptorProto) }
-inline upb::reffed_ptr<const upb::FieldDef> default_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_default_value) }
-inline upb::reffed_ptr<const upb::FieldDef> extendee() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_extendee) }
-inline upb::reffed_ptr<const upb::FieldDef> label() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_label) }
-inline upb::reffed_ptr<const upb::FieldDef> name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_name) }
-inline upb::reffed_ptr<const upb::FieldDef> number() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_number) }
-inline upb::reffed_ptr<const upb::FieldDef> options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_options) }
-inline upb::reffed_ptr<const upb::FieldDef> type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_type) }
-inline upb::reffed_ptr<const upb::FieldDef> type_name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_type_name) }
-inline upb::reffed_ptr<const upb::EnumDef> Label() { RETURN_REFFED(upb::EnumDef, upbdefs_google_protobuf_FieldDescriptorProto_Label) }
-inline upb::reffed_ptr<const upb::EnumDef> Type() { RETURN_REFFED(upb::EnumDef, upbdefs_google_protobuf_FieldDescriptorProto_Type) }
-}  /* namespace FieldDescriptorProto */
-}  /* namespace protobuf */
-}  /* namespace google */
-
-namespace google {
-namespace protobuf {
-namespace FieldOptions {
-inline upb::reffed_ptr<const upb::MessageDef> MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_FieldOptions) }
-inline upb::reffed_ptr<const upb::FieldDef> ctype() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_ctype) }
-inline upb::reffed_ptr<const upb::FieldDef> deprecated() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_deprecated) }
-inline upb::reffed_ptr<const upb::FieldDef> experimental_map_key() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_experimental_map_key) }
-inline upb::reffed_ptr<const upb::FieldDef> lazy() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_lazy) }
-inline upb::reffed_ptr<const upb::FieldDef> packed() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_packed) }
-inline upb::reffed_ptr<const upb::FieldDef> uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_uninterpreted_option) }
-inline upb::reffed_ptr<const upb::FieldDef> weak() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_weak) }
-inline upb::reffed_ptr<const upb::EnumDef> CType() { RETURN_REFFED(upb::EnumDef, upbdefs_google_protobuf_FieldOptions_CType) }
-}  /* namespace FieldOptions */
-}  /* namespace protobuf */
-}  /* namespace google */
-
-namespace google {
-namespace protobuf {
-namespace FileDescriptorProto {
-inline upb::reffed_ptr<const upb::MessageDef> MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_FileDescriptorProto) }
-inline upb::reffed_ptr<const upb::FieldDef> dependency() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_dependency) }
-inline upb::reffed_ptr<const upb::FieldDef> enum_type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_enum_type) }
-inline upb::reffed_ptr<const upb::FieldDef> extension() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_extension) }
-inline upb::reffed_ptr<const upb::FieldDef> message_type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_message_type) }
-inline upb::reffed_ptr<const upb::FieldDef> name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_name) }
-inline upb::reffed_ptr<const upb::FieldDef> options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_options) }
-inline upb::reffed_ptr<const upb::FieldDef> package() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_package) }
-inline upb::reffed_ptr<const upb::FieldDef> public_dependency() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_public_dependency) }
-inline upb::reffed_ptr<const upb::FieldDef> service() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_service) }
-inline upb::reffed_ptr<const upb::FieldDef> source_code_info() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_source_code_info) }
-inline upb::reffed_ptr<const upb::FieldDef> weak_dependency() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_weak_dependency) }
-}  /* namespace FileDescriptorProto */
-}  /* namespace protobuf */
-}  /* namespace google */
-
-namespace google {
-namespace protobuf {
-namespace FileDescriptorSet {
-inline upb::reffed_ptr<const upb::MessageDef> MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_FileDescriptorSet) }
-inline upb::reffed_ptr<const upb::FieldDef> file() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorSet_file) }
-}  /* namespace FileDescriptorSet */
-}  /* namespace protobuf */
-}  /* namespace google */
-
-namespace google {
-namespace protobuf {
-namespace FileOptions {
-inline upb::reffed_ptr<const upb::MessageDef> MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_FileOptions) }
-inline upb::reffed_ptr<const upb::FieldDef> cc_generic_services() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_cc_generic_services) }
-inline upb::reffed_ptr<const upb::FieldDef> go_package() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_go_package) }
-inline upb::reffed_ptr<const upb::FieldDef> java_generate_equals_and_hash() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_java_generate_equals_and_hash) }
-inline upb::reffed_ptr<const upb::FieldDef> java_generic_services() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_java_generic_services) }
-inline upb::reffed_ptr<const upb::FieldDef> java_multiple_files() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_java_multiple_files) }
-inline upb::reffed_ptr<const upb::FieldDef> java_outer_classname() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_java_outer_classname) }
-inline upb::reffed_ptr<const upb::FieldDef> java_package() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_java_package) }
-inline upb::reffed_ptr<const upb::FieldDef> optimize_for() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_optimize_for) }
-inline upb::reffed_ptr<const upb::FieldDef> py_generic_services() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_py_generic_services) }
-inline upb::reffed_ptr<const upb::FieldDef> uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_uninterpreted_option) }
-inline upb::reffed_ptr<const upb::EnumDef> OptimizeMode() { RETURN_REFFED(upb::EnumDef, upbdefs_google_protobuf_FileOptions_OptimizeMode) }
-}  /* namespace FileOptions */
-}  /* namespace protobuf */
-}  /* namespace google */
-
-namespace google {
-namespace protobuf {
-namespace MessageOptions {
-inline upb::reffed_ptr<const upb::MessageDef> MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_MessageOptions) }
-inline upb::reffed_ptr<const upb::FieldDef> message_set_wire_format() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MessageOptions_message_set_wire_format) }
-inline upb::reffed_ptr<const upb::FieldDef> no_standard_descriptor_accessor() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MessageOptions_no_standard_descriptor_accessor) }
-inline upb::reffed_ptr<const upb::FieldDef> uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MessageOptions_uninterpreted_option) }
-}  /* namespace MessageOptions */
-}  /* namespace protobuf */
-}  /* namespace google */
-
-namespace google {
-namespace protobuf {
-namespace MethodDescriptorProto {
-inline upb::reffed_ptr<const upb::MessageDef> MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_MethodDescriptorProto) }
-inline upb::reffed_ptr<const upb::FieldDef> input_type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MethodDescriptorProto_input_type) }
-inline upb::reffed_ptr<const upb::FieldDef> name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MethodDescriptorProto_name) }
-inline upb::reffed_ptr<const upb::FieldDef> options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MethodDescriptorProto_options) }
-inline upb::reffed_ptr<const upb::FieldDef> output_type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MethodDescriptorProto_output_type) }
-}  /* namespace MethodDescriptorProto */
-}  /* namespace protobuf */
-}  /* namespace google */
-
-namespace google {
-namespace protobuf {
-namespace MethodOptions {
-inline upb::reffed_ptr<const upb::MessageDef> MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_MethodOptions) }
-inline upb::reffed_ptr<const upb::FieldDef> uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MethodOptions_uninterpreted_option) }
-}  /* namespace MethodOptions */
-}  /* namespace protobuf */
-}  /* namespace google */
-
-namespace google {
-namespace protobuf {
-namespace ServiceDescriptorProto {
-inline upb::reffed_ptr<const upb::MessageDef> MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_ServiceDescriptorProto) }
-inline upb::reffed_ptr<const upb::FieldDef> method() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_ServiceDescriptorProto_method) }
-inline upb::reffed_ptr<const upb::FieldDef> name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_ServiceDescriptorProto_name) }
-inline upb::reffed_ptr<const upb::FieldDef> options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_ServiceDescriptorProto_options) }
-}  /* namespace ServiceDescriptorProto */
-}  /* namespace protobuf */
-}  /* namespace google */
-
-namespace google {
-namespace protobuf {
-namespace ServiceOptions {
-inline upb::reffed_ptr<const upb::MessageDef> MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_ServiceOptions) }
-inline upb::reffed_ptr<const upb::FieldDef> uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_ServiceOptions_uninterpreted_option) }
-}  /* namespace ServiceOptions */
-}  /* namespace protobuf */
-}  /* namespace google */
-
-namespace google {
-namespace protobuf {
-namespace SourceCodeInfo {
-inline upb::reffed_ptr<const upb::MessageDef> MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_SourceCodeInfo) }
-inline upb::reffed_ptr<const upb::FieldDef> location() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_SourceCodeInfo_location) }
-}  /* namespace SourceCodeInfo */
-}  /* namespace protobuf */
-}  /* namespace google */
-
-namespace google {
-namespace protobuf {
-namespace SourceCodeInfo {
-namespace Location {
-inline upb::reffed_ptr<const upb::MessageDef> MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_SourceCodeInfo_Location) }
-inline upb::reffed_ptr<const upb::FieldDef> leading_comments() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_SourceCodeInfo_Location_leading_comments) }
-inline upb::reffed_ptr<const upb::FieldDef> path() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_SourceCodeInfo_Location_path) }
-inline upb::reffed_ptr<const upb::FieldDef> span() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_SourceCodeInfo_Location_span) }
-inline upb::reffed_ptr<const upb::FieldDef> trailing_comments() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_SourceCodeInfo_Location_trailing_comments) }
-}  /* namespace Location */
-}  /* namespace SourceCodeInfo */
-}  /* namespace protobuf */
-}  /* namespace google */
-
-namespace google {
-namespace protobuf {
-namespace UninterpretedOption {
-inline upb::reffed_ptr<const upb::MessageDef> MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_UninterpretedOption) }
-inline upb::reffed_ptr<const upb::FieldDef> aggregate_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_aggregate_value) }
-inline upb::reffed_ptr<const upb::FieldDef> double_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_double_value) }
-inline upb::reffed_ptr<const upb::FieldDef> identifier_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_identifier_value) }
-inline upb::reffed_ptr<const upb::FieldDef> name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_name) }
-inline upb::reffed_ptr<const upb::FieldDef> negative_int_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_negative_int_value) }
-inline upb::reffed_ptr<const upb::FieldDef> positive_int_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_positive_int_value) }
-inline upb::reffed_ptr<const upb::FieldDef> string_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_string_value) }
-}  /* namespace UninterpretedOption */
-}  /* namespace protobuf */
-}  /* namespace google */
-
-namespace google {
-namespace protobuf {
-namespace UninterpretedOption {
-namespace NamePart {
-inline upb::reffed_ptr<const upb::MessageDef> MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_UninterpretedOption_NamePart) }
-inline upb::reffed_ptr<const upb::FieldDef> is_extension() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_NamePart_is_extension) }
-inline upb::reffed_ptr<const upb::FieldDef> name_part() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_NamePart_name_part) }
-}  /* namespace NamePart */
-}  /* namespace UninterpretedOption */
-}  /* namespace protobuf */
-}  /* namespace google */
-
 }  /* namespace upbdefs */
 
+#endif  /* __cplusplus */
 
-#undef RETURN_REFFED
-#endif /* __cplusplus */
-
-#endif  /* GOOGLE_PROTOBUF_DESCRIPTOR_UPB_H_ */
+#endif  /* UPB_DESCRIPTOR_DESCRIPTOR_PROTO_UPB_H_ */
 /*
 ** Internal-only definitions for the decoder.
 */
@@ -7030,7 +7339,6 @@
 #ifndef UPB_DECODER_INT_H_
 #define UPB_DECODER_INT_H_
 
-#include <stdlib.h>
 /*
 ** upb::pb::Decoder
 **
@@ -7067,6 +7375,13 @@
 UPB_DECLARE_DERIVED_TYPE(upb::pb::DecoderMethod, upb::RefCounted,
                          upb_pbdecodermethod, upb_refcounted)
 
+/* The maximum number of bytes we are required to buffer internally between
+ * calls to the decoder.  The value is 14: a 5 byte unknown tag plus ten-byte
+ * varint, less one because we are buffering an incomplete value.
+ *
+ * Should only be used by unit tests. */
+#define UPB_DECODER_MAX_RESIDUAL_BYTES 14
+
 #ifdef __cplusplus
 
 /* The parameters one uses to construct a DecoderMethod.
@@ -7123,7 +7438,7 @@
  * constructed.  This hint may be an overestimate for some build configurations.
  * But if the decoder library is upgraded without recompiling the application,
  * it may be an underestimate. */
-#define UPB_PB_DECODER_SIZE 4408
+#define UPB_PB_DECODER_SIZE 4416
 
 #ifdef __cplusplus
 
@@ -7541,11 +7856,8 @@
   /* Overall stream offset of "buf." */
   uint64_t bufstart_ofs;
 
-  /* Buffer for residual bytes not parsed from the previous buffer.
-   * The maximum number of residual bytes we require is 12; a five-byte
-   * unknown tag plus an eight-byte value, less one because the value
-   * is only a partial value. */
-  char residual[12];
+  /* Buffer for residual bytes not parsed from the previous buffer. */
+  char residual[UPB_DECODER_MAX_RESIDUAL_BYTES];
   char *residual_end;
 
   /* Bytes of data that should be discarded from the input beore we start
@@ -7812,9 +8124,9 @@
   char buf[UPB_PB_VARINT_MAX_LEN];
   size_t bytes = upb_vencode64(val, buf);
   uint64_t ret = 0;
-  assert(bytes <= 5);
+  UPB_ASSERT(bytes <= 5);
   memcpy(&ret, buf, bytes);
-  assert(ret <= 0xffffffffffU);
+  UPB_ASSERT(ret <= 0xffffffffffU);
   return ret;
 }
 
@@ -7939,49 +8251,44 @@
 #include <stdbool.h>
 
 #ifdef __cplusplus
+#include <vector>
+
 extern "C" {
 #endif
 
-/* Loads all defs from the given protobuf binary descriptor, setting default
- * accessors and a default layout on all messages.  The caller owns the
- * returned array of defs, which will be of length *n.  On error NULL is
- * returned and status is set (if non-NULL). */
-upb_def **upb_load_defs_from_descriptor(const char *str, size_t len, int *n,
-                                        void *owner, upb_status *status);
-
-/* Like the previous but also adds the loaded defs to the given symtab. */
-bool upb_load_descriptor_into_symtab(upb_symtab *symtab, const char *str,
-                                     size_t len, upb_status *status);
-
-/* Like the previous but also reads the descriptor from the given filename. */
-bool upb_load_descriptor_file_into_symtab(upb_symtab *symtab, const char *fname,
-                                          upb_status *status);
-
-/* Reads the given filename into a character string, returning NULL if there
- * was an error. */
-char *upb_readfile(const char *filename, size_t *len);
+/* Loads a binary descriptor and returns a NULL-terminated array of unfrozen
+ * filedefs.  The caller owns the returned array, which must be freed with
+ * upb_gfree(). */
+upb_filedef **upb_loaddescriptor(const char *buf, size_t n, const void *owner,
+                                 upb_status *status);
 
 #ifdef __cplusplus
 }  /* extern "C" */
 
 namespace upb {
 
-/* All routines that load descriptors expect the descriptor to be a
- * FileDescriptorSet. */
-inline bool LoadDescriptorFileIntoSymtab(SymbolTable* s, const char *fname,
-                                         Status* status) {
-  return upb_load_descriptor_file_into_symtab(s, fname, status);
-}
+inline bool LoadDescriptor(const char* buf, size_t n, Status* status,
+                           std::vector<reffed_ptr<FileDef> >* files) {
+  FileDef** parsed_files = upb_loaddescriptor(buf, n, &parsed_files, status);
 
-inline bool LoadDescriptorIntoSymtab(SymbolTable* s, const char* str,
-                                     size_t len, Status* status) {
-  return upb_load_descriptor_into_symtab(s, str, len, status);
+  if (parsed_files) {
+    FileDef** p = parsed_files;
+    while (*p) {
+      files->push_back(reffed_ptr<FileDef>(*p, &parsed_files));
+      ++p;
+    }
+    free(parsed_files);
+    return true;
+  } else {
+    return false;
+  }
 }
 
 /* Templated so it can accept both string and std::string. */
 template <typename T>
-bool LoadDescriptorIntoSymtab(SymbolTable* s, const T& desc, Status* status) {
-  return upb_load_descriptor_into_symtab(s, desc.c_str(), desc.size(), status);
+bool LoadDescriptor(const T& desc, Status* status,
+                    std::vector<reffed_ptr<FileDef> >* files) {
+  return LoadDescriptor(desc.c_str(), desc.size(), status, files);
 }
 
 }  /* namespace upb */
@@ -8083,11 +8390,14 @@
 namespace upb {
 namespace json {
 class Parser;
+class ParserMethod;
 }  /* namespace json */
 }  /* namespace upb */
 #endif
 
 UPB_DECLARE_TYPE(upb::json::Parser, upb_json_parser)
+UPB_DECLARE_DERIVED_TYPE(upb::json::ParserMethod, upb::RefCounted,
+                         upb_json_parsermethod, upb_refcounted)
 
 /* upb::json::Parser **********************************************************/
 
@@ -8095,7 +8405,7 @@
  * constructed.  This hint may be an overestimate for some build configurations.
  * But if the parser library is upgraded without recompiling the application,
  * it may be an underestimate. */
-#define UPB_JSON_PARSER_SIZE 3704
+#define UPB_JSON_PARSER_SIZE 4112
 
 #ifdef __cplusplus
 
@@ -8103,7 +8413,8 @@
  * sink. */
 class upb::json::Parser {
  public:
-  static Parser* Create(Environment* env, Sink* output);
+  static Parser* Create(Environment* env, const ParserMethod* method,
+                        Sink* output);
 
   BytesSink* input();
 
@@ -8111,25 +8422,72 @@
   UPB_DISALLOW_POD_OPS(Parser, upb::json::Parser)
 };
 
+class upb::json::ParserMethod {
+ public:
+  /* Include base methods from upb::ReferenceCounted. */
+  UPB_REFCOUNTED_CPPMETHODS
+
+  /* Returns handlers for parsing according to the specified schema. */
+  static reffed_ptr<const ParserMethod> New(const upb::MessageDef* md);
+
+  /* The destination handlers that are statically bound to this method.
+   * This method is only capable of outputting to a sink that uses these
+   * handlers. */
+  const Handlers* dest_handlers() const;
+
+  /* The input handlers for this decoder method. */
+  const BytesHandler* input_handler() const;
+
+ private:
+  UPB_DISALLOW_POD_OPS(ParserMethod, upb::json::ParserMethod)
+};
+
 #endif
 
 UPB_BEGIN_EXTERN_C
 
-upb_json_parser *upb_json_parser_create(upb_env *e, upb_sink *output);
+upb_json_parser* upb_json_parser_create(upb_env* e,
+                                        const upb_json_parsermethod* m,
+                                        upb_sink* output);
 upb_bytessink *upb_json_parser_input(upb_json_parser *p);
 
+upb_json_parsermethod* upb_json_parsermethod_new(const upb_msgdef* md,
+                                                 const void* owner);
+const upb_handlers *upb_json_parsermethod_desthandlers(
+    const upb_json_parsermethod *m);
+const upb_byteshandler *upb_json_parsermethod_inputhandler(
+    const upb_json_parsermethod *m);
+
+/* Include refcounted methods like upb_json_parsermethod_ref(). */
+UPB_REFCOUNTED_CMETHODS(upb_json_parsermethod, upb_json_parsermethod_upcast)
+
 UPB_END_EXTERN_C
 
 #ifdef __cplusplus
 
 namespace upb {
 namespace json {
-inline Parser* Parser::Create(Environment* env, Sink* output) {
-  return upb_json_parser_create(env, output);
+inline Parser* Parser::Create(Environment* env, const ParserMethod* method,
+                              Sink* output) {
+  return upb_json_parser_create(env, method, output);
 }
 inline BytesSink* Parser::input() {
   return upb_json_parser_input(this);
 }
+
+inline const Handlers* ParserMethod::dest_handlers() const {
+  return upb_json_parsermethod_desthandlers(this);
+}
+inline const BytesHandler* ParserMethod::input_handler() const {
+  return upb_json_parsermethod_inputhandler(this);
+}
+/* static */
+inline reffed_ptr<const ParserMethod> ParserMethod::New(
+    const MessageDef* md) {
+  const upb_json_parsermethod *m = upb_json_parsermethod_new(md, &m);
+  return reffed_ptr<const ParserMethod>(m, &m);
+}
+
 }  /* namespace json */
 }  /* namespace upb */
 
@@ -8160,7 +8518,7 @@
 
 /* upb::json::Printer *********************************************************/
 
-#define UPB_JSON_PRINTER_SIZE 168
+#define UPB_JSON_PRINTER_SIZE 176
 
 #ifdef __cplusplus
 
@@ -8173,8 +8531,12 @@
   /* The input to the printer. */
   Sink* input();
 
-  /* Returns handlers for printing according to the specified schema. */
-  static reffed_ptr<const Handlers> NewHandlers(const upb::MessageDef* md);
+  /* Returns handlers for printing according to the specified schema.
+   * If preserve_proto_fieldnames is true, the output JSON will use the
+   * original .proto field names (ie. {"my_field":3}) instead of using
+   * camelCased names, which is the default: (eg. {"myField":3}). */
+  static reffed_ptr<const Handlers> NewHandlers(const upb::MessageDef* md,
+                                                bool preserve_proto_fieldnames);
 
   static const size_t kSize = UPB_JSON_PRINTER_SIZE;
 
@@ -8191,6 +8553,7 @@
                                           upb_bytessink *output);
 upb_sink *upb_json_printer_input(upb_json_printer *p);
 const upb_handlers *upb_json_printer_newhandlers(const upb_msgdef *md,
+                                                 bool preserve_fieldnames,
                                                  const void *owner);
 
 UPB_END_EXTERN_C
@@ -8205,8 +8568,9 @@
 }
 inline Sink* Printer::input() { return upb_json_printer_input(this); }
 inline reffed_ptr<const Handlers> Printer::NewHandlers(
-    const upb::MessageDef *md) {
-  const Handlers* h = upb_json_printer_newhandlers(md, &h);
+    const upb::MessageDef *md, bool preserve_proto_fieldnames) {
+  const Handlers* h = upb_json_printer_newhandlers(
+      md, preserve_proto_fieldnames, &h);
   return reffed_ptr<const Handlers>(h, &h);
 }
 }  /* namespace json */
diff --git a/php/ext/google/protobuf/utf8.c b/php/ext/google/protobuf/utf8.c
new file mode 100644
index 0000000..a154163
--- /dev/null
+++ b/php/ext/google/protobuf/utf8.c
@@ -0,0 +1,68 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "utf8.h"
+
+static const uint8_t utf8_offset[] = {
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+    2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+    4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+bool is_structurally_valid_utf8(const char* buf, int len) {
+  int i, j;
+  uint8_t offset;
+
+  i = 0;
+  while (i < len) {
+    offset = utf8_offset[(uint8_t)buf[i]];
+    if (offset == 0 || i + offset > len) {
+      return false;
+    }
+    for (j = i + 1; j < i + offset; j++) {
+      if (buf[j] & 0xc0 != 0x80) {
+        return false;
+      }
+    }
+    i += offset;
+  }
+  return i == len;
+}
diff --git a/php/ext/google/protobuf/utf8.h b/php/ext/google/protobuf/utf8.h
new file mode 100644
index 0000000..28b8d87
--- /dev/null
+++ b/php/ext/google/protobuf/utf8.h
@@ -0,0 +1,36 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef GOOGLE_PROTOBUF_UTF8_H_
+#define GOOGLE_PROTOBUF_UTF8_H_
+
+bool is_structurally_valid_utf8(const char* buf, int len);
+
+#endif  // GOOGLE_PROTOBUF_UTF8_H_
diff --git a/php/src/Google/Protobuf/Internal/DescriptorPool.php b/php/src/Google/Protobuf/Internal/DescriptorPool.php
new file mode 100644
index 0000000..23b304a
--- /dev/null
+++ b/php/src/Google/Protobuf/Internal/DescriptorPool.php
@@ -0,0 +1,162 @@
+<?php
+
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+namespace Google\Protobuf\Internal;
+
+use Google\Protobuf\Internal\Descriptor;
+use Google\Protobuf\Internal\FileDescriptor;
+use Google\Protobuf\Internal\FileDescriptorSet;
+use Google\Protobuf\Internal\MessageBuilderContext;
+use Google\Protobuf\Internal\EnumBuilderContext;
+
+class DescriptorPool
+{
+    private static $pool;
+    // Map from message names to sub-maps, which are maps from field numbers to
+    // field descriptors.
+    private $class_to_desc = [];
+    private $class_to_enum_desc = [];
+    private $proto_to_class = [];
+
+    public static function getGeneratedPool()
+    {
+        if (!isset(self::$pool)) {
+            self::$pool = new DescriptorPool();
+        }
+        return self::$pool;
+    }
+
+    public function internalAddGeneratedFile($data)
+    {
+        $files = new FileDescriptorSet();
+        $files->decode($data);
+        $file = FileDescriptor::buildFromProto($files->getFile()[0]);
+
+        foreach ($file->getMessageType() as &$desc) {
+            $this->addDescriptor($desc);
+        }
+        unset($desc);
+
+        foreach ($file->getEnumType() as &$desc) {
+            $this->addEnumDescriptor($desc);
+        }
+        unset($desc);
+
+        foreach ($file->getMessageType() as &$desc) {
+            $this->crossLink($desc);
+        }
+        unset($desc);
+    }
+
+    public function addMessage($name, $klass)
+    {
+        return new MessageBuilderContext($name, $klass, $this);
+    }
+
+    public function addEnum($name, $klass)
+    {
+        return new EnumBuilderContext($name, $klass, $this);
+    }
+
+    public function addDescriptor($descriptor)
+    {
+        $this->proto_to_class[$descriptor->getFullName()] =
+            $descriptor->getClass();
+        $this->class_to_desc[$descriptor->getClass()] = $descriptor;
+        foreach ($descriptor->getNestedType() as $nested_type) {
+            $this->addDescriptor($nested_type);
+        }
+    }
+
+    public function addEnumDescriptor($descriptor)
+    {
+        $this->proto_to_class[$descriptor->getFullName()] =
+            $descriptor->getClass();
+        $this->class_to_enum_desc[$descriptor->getClass()] = $descriptor;
+    }
+
+    public function getDescriptorByClassName($klass)
+    {
+        return $this->class_to_desc[$klass];
+    }
+
+    public function getEnumDescriptorByClassName($klass)
+    {
+        return $this->class_to_enum_desc[$klass];
+    }
+
+    public function getDescriptorByProtoName($proto)
+    {
+        $klass = $this->proto_to_class[$proto];
+        return $this->class_to_desc[$klass];
+    }
+
+    public function getEnumDescriptorByProtoName($proto)
+    {
+        $klass = $this->proto_to_class[$proto];
+        return $this->class_to_enum_desc[$klass];
+    }
+
+    private function crossLink(&$desc)
+    {
+        foreach ($desc->getField() as &$field) {
+            switch ($field->getType()) {
+                case GPBType::MESSAGE:
+                    $proto = $field->getMessageType();
+                    $field->setMessageType(
+                        $this->getDescriptorByProtoName($proto));
+                    break;
+                case GPBType::ENUM:
+                    $proto = $field->getEnumType();
+                    $field->setEnumType(
+                        $this->getEnumDescriptorByProtoName($proto));
+                    break;
+                default:
+                    break;
+            }
+        }
+        unset($field);
+
+        foreach ($desc->getNestedType() as &$nested_type) {
+            $this->crossLink($nested_type);
+        }
+        unset($nested_type);
+    }
+
+    public function finish()
+    {
+        foreach ($this->class_to_desc as $klass => &$desc) {
+            $this->crossLink($desc);
+        }
+        unset($desc);
+    }
+}
diff --git a/php/src/Google/Protobuf/Internal/EnumBuilderContext.php b/php/src/Google/Protobuf/Internal/EnumBuilderContext.php
new file mode 100644
index 0000000..c1dac24
--- /dev/null
+++ b/php/src/Google/Protobuf/Internal/EnumBuilderContext.php
@@ -0,0 +1,63 @@
+<?php
+
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+namespace Google\Protobuf\Internal;
+
+use Google\Protobuf\Internal\EnumDescriptor;
+use Google\Protobuf\Internal\EnumValueDescriptor;
+
+class EnumBuilderContext
+{
+
+    private $descriptor;
+    private $pool;
+
+    public function __construct($full_name, $klass, $pool)
+    {
+        $this->descriptor = new EnumDescriptor();
+        $this->descriptor->setFullName($full_name);
+        $this->descriptor->setClass($klass);
+        $this->pool = $pool;
+    }
+
+    public function value($name, $number)
+    {
+        $value = new EnumValueDescriptor();
+        $this->descriptor->addValue($number, $value);
+        return $this;
+    }
+
+    public function finalizeToPool()
+    {
+        $this->pool->addEnumDescriptor($this->descriptor);
+    }
+}
diff --git a/php/src/Google/Protobuf/Internal/GPBLabel.php b/php/src/Google/Protobuf/Internal/GPBLabel.php
new file mode 100644
index 0000000..0fb2384
--- /dev/null
+++ b/php/src/Google/Protobuf/Internal/GPBLabel.php
@@ -0,0 +1,40 @@
+<?php
+
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+namespace Google\Protobuf\Internal;
+
+class GPBLabel
+{
+    const OPTIONAL = 1;
+    const REQUIRED = 2;
+    const REPEATED = 3;
+}
diff --git a/php/src/Google/Protobuf/Internal/GPBType.php b/php/src/Google/Protobuf/Internal/GPBType.php
new file mode 100644
index 0000000..fa849ce
--- /dev/null
+++ b/php/src/Google/Protobuf/Internal/GPBType.php
@@ -0,0 +1,55 @@
+<?php
+
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+namespace Google\Protobuf\Internal;
+
+class GPBType
+{
+    const DOUBLE   =  1;
+    const FLOAT    =  2;
+    const INT64    =  3;
+    const UINT64   =  4;
+    const INT32    =  5;
+    const FIXED64  =  6;
+    const FIXED32  =  7;
+    const BOOL     =  8;
+    const STRING   =  9;
+    const GROUP    = 10;
+    const MESSAGE  = 11;
+    const BYTES    = 12;
+    const UINT32   = 13;
+    const ENUM     = 14;
+    const SFIXED32 = 15;
+    const SFIXED64 = 16;
+    const SINT32   = 17;
+    const SINT64   = 18;
+}
diff --git a/php/src/Google/Protobuf/Internal/GPBUtil.php b/php/src/Google/Protobuf/Internal/GPBUtil.php
new file mode 100644
index 0000000..417a972
--- /dev/null
+++ b/php/src/Google/Protobuf/Internal/GPBUtil.php
@@ -0,0 +1,161 @@
+<?php
+
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+namespace Google\Protobuf\Internal;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+
+class GPBUtil
+{
+
+    public static function checkString(&$var, $check_utf8)
+    {
+        if (is_array($var) || is_object($var)) {
+            trigger_error("Expect string.", E_USER_ERROR);
+            return;
+        }
+        if (!is_string($var)) {
+            $var = strval($var);
+        }
+        if ($check_utf8 && !preg_match('//u', $var)) {
+            trigger_error("Expect utf-8 encoding.", E_USER_ERROR);
+            return;
+        }
+    }
+
+    public static function checkEnum(&$var)
+    {
+      static::checkInt32($var);
+    }
+
+    public static function checkInt32(&$var)
+    {
+        if (is_numeric($var)) {
+            $var = intval($var);
+        } else {
+            trigger_error("Expect integer.", E_USER_ERROR);
+        }
+    }
+
+    public static function checkUint32(&$var)
+    {
+        if (is_numeric($var)) {
+            $var = intval($var);
+            if (PHP_INT_SIZE === 8) {
+                $var |= ((-(($var >> 31) & 0x1)) & ~0xFFFFFFFF);
+            }
+        } else {
+            trigger_error("Expect integer.", E_USER_ERROR);
+        }
+    }
+
+    public static function checkInt64(&$var)
+    {
+        if (is_numeric($var)) {
+            $var = intval($var);
+        } else {
+            trigger_error("Expect integer.", E_USER_ERROR);
+        }
+    }
+
+    public static function checkUint64(&$var)
+    {
+        if (is_numeric($var)) {
+            $var = intval($var);
+        } else {
+            trigger_error("Expect integer.", E_USER_ERROR);
+        }
+    }
+
+    public static function checkFloat(&$var)
+    {
+        if (is_float($var) || is_numeric($var)) {
+            $var = floatval($var);
+        } else {
+            trigger_error("Expect float.", E_USER_ERROR);
+        }
+    }
+
+    public static function checkDouble(&$var)
+    {
+        if (is_float($var) || is_numeric($var)) {
+            $var = floatval($var);
+        } else {
+            trigger_error("Expect float.", E_USER_ERROR);
+        }
+    }
+
+    public static function checkBool(&$var)
+    {
+        if (is_array($var) || is_object($var)) {
+            trigger_error("Expect boolean.", E_USER_ERROR);
+            return;
+        }
+        $var = boolval($var);
+    }
+
+    public static function checkMessage(&$var, $klass)
+    {
+        if (!$var instanceof $klass && !is_null($var)) {
+            trigger_error("Expect message.", E_USER_ERROR);
+        }
+    }
+
+    public static function checkRepeatedField(&$var, $type, $klass = null)
+    {
+        if (!$var instanceof RepeatedField) {
+            trigger_error("Expect repeated field.", E_USER_ERROR);
+        }
+        if ($var->getType() != $type) {
+            trigger_error(
+                "Expect repeated field of different type.",
+                E_USER_ERROR);
+        }
+        if ($var->getType() === GPBType::MESSAGE &&
+            $var->getClass() !== $klass) {
+            trigger_error(
+                "Expect repeated field of different message.",
+                E_USER_ERROR);
+        }
+    }
+
+    public static function Int64($value)
+    {
+        return new Int64($value);
+    }
+
+    public static function Uint64($value)
+    {
+        return new Uint64($value);
+    }
+}
diff --git a/php/src/Google/Protobuf/Internal/GPBWire.php b/php/src/Google/Protobuf/Internal/GPBWire.php
new file mode 100644
index 0000000..0e741e1
--- /dev/null
+++ b/php/src/Google/Protobuf/Internal/GPBWire.php
@@ -0,0 +1,583 @@
+<?php
+
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+namespace Google\Protobuf\Internal;
+
+use Google\Protobuf\Internal\GPBUtil;
+use Google\Protobuf\Internal\Int64;
+use Google\Protobuf\Internal\Uint64;
+
+class GPBWire
+{
+
+    const TAG_TYPE_BITS = 3;
+
+    const WIRETYPE_VARINT  = 0;
+    const WIRETYPE_FIXED64 = 1;
+    const WIRETYPE_LENGTH_DELIMITED = 2;
+    const WIRETYPE_START_GROUP = 3;
+    const WIRETYPE_END_GROUP = 4;
+    const WIRETYPE_FIXED32 = 5;
+
+    const UNKNOWN = 0;
+    const NORMAL_FORMAT = 1;
+    const PACKED_FORMAT = 2;
+
+    public static function getTagFieldNumber($tag)
+    {
+        return ($tag >> self::TAG_TYPE_BITS) &
+            (1 << ((PHP_INT_SIZE * 8) - self::TAG_TYPE_BITS)) - 1;
+    }
+
+    public static function getTagWireType($tag)
+    {
+        return $tag & 0x7;
+    }
+
+    public static function getWireType($type)
+    {
+        switch ($type) {
+            case GPBType::FLOAT:
+            case GPBType::FIXED32:
+            case GPBType::SFIXED32:
+                return self::WIRETYPE_FIXED32;
+            case GPBType::DOUBLE:
+            case GPBType::FIXED64:
+            case GPBType::SFIXED64:
+                return self::WIRETYPE_FIXED64;
+            case GPBType::UINT32:
+            case GPBType::UINT64:
+            case GPBType::INT32:
+            case GPBType::INT64:
+            case GPBType::SINT32:
+            case GPBType::SINT64:
+            case GPBType::ENUM:
+            case GPBType::BOOL:
+                return self::WIRETYPE_VARINT;
+            case GPBType::STRING:
+            case GPBType::BYTES:
+            case GPBType::MESSAGE:
+                return self::WIRETYPE_LENGTH_DELIMITED;
+            case GPBType::GROUP:
+                user_error("Unsupported type.");
+                return 0;
+            default:
+                user_error("Unsupported type.");
+                return 0;
+        }
+    }
+
+  // ZigZag Transform:  Encodes signed integers so that they can be effectively
+  // used with varint encoding.
+  //
+  // varint operates on unsigned integers, encoding smaller numbers into fewer
+  // bytes.  If you try to use it on a signed integer, it will treat this
+  // number as a very large unsigned integer, which means that even small
+  // signed numbers like -1 will take the maximum number of bytes (10) to
+  // encode.  zigZagEncode() maps signed integers to unsigned in such a way
+  // that those with a small absolute value will have smaller encoded values,
+  // making them appropriate for encoding using varint.
+  //
+  // int32 ->     uint32
+  // -------------------------
+  //           0 ->          0
+  //          -1 ->          1
+  //           1 ->          2
+  //          -2 ->          3
+  //         ... ->        ...
+  //  2147483647 -> 4294967294
+  // -2147483648 -> 4294967295
+  //
+  //        >> encode >>
+  //        << decode <<
+  public static function zigZagEncode32($int32)
+  {
+      // Fill high 32 bits.
+      if (PHP_INT_SIZE === 8) {
+          $int32 |= ((($int32 << 32) >> 31) & (0xFFFFFFFF << 32));
+      }
+
+      $uint32 = ($int32 << 1) ^ ($int32 >> 31);
+
+      // Fill high 32 bits.
+      if (PHP_INT_SIZE === 8) {
+          $uint32 |= ((($uint32 << 32) >> 31) & (0xFFFFFFFF << 32));
+      }
+
+      return $uint32;
+  }
+
+    public static function zigZagDecode32($uint32)
+    {
+        // Fill high 32 bits.
+        if (PHP_INT_SIZE === 8) {
+            $uint32 |= ($uint32 & 0xFFFFFFFF);
+        }
+
+        $int32 = (($uint32 >> 1) & 0x7FFFFFFF) ^ (-($uint32 & 1));
+
+        return $int32;
+    }
+
+    public static function zigZagEncode64($int64)
+    {
+        $a = $int64->copy()->leftShift(1);
+        $b = $int64->copy()->rightShift(63);
+        $result = $a->bitXor($b);
+        $uint64 = Uint64::newValue($result->high, $result->low);
+        return $uint64;
+    }
+
+    public static function zigZagDecode64($uint64)
+    {
+        $a = $uint64->copy()->rightShift(1);
+        $b = $uint64->oddMask();
+        $result = $a->bitXor($b);
+        $int64 = Int64::newValue($result->high, $result->low);
+        return $int64;
+    }
+
+    public static function readInt32(&$input, &$value)
+    {
+        return $input->readVarint32($value);
+    }
+
+    public static function readInt64(&$input, &$value)
+    {
+        return $input->readVarint64($value);
+    }
+
+    public static function readUint32(&$input, &$value)
+    {
+        return self::readInt32($input, $value);
+    }
+
+    public static function readUint64(&$input, &$value)
+    {
+        return self::readInt64($input, $value);
+    }
+
+    public static function readSint32(&$input, &$value)
+    {
+        if (!$input->readVarint32($value)) {
+            return false;
+        }
+        $value = GPBWire::zigZagDecode32($value);
+        return true;
+    }
+
+    public static function readSint64(&$input, &$value)
+    {
+        if (!$input->readVarint64($value)) {
+            return false;
+        }
+        $value = GPBWire::zigZagDecode64($value);
+        return true;
+    }
+
+    public static function readFixed32(&$input, &$value)
+    {
+        return $input->readLittleEndian32($value);
+    }
+
+    public static function readFixed64(&$input, &$value)
+    {
+        return $input->readLittleEndian64($value);
+    }
+
+    public static function readSfixed32(&$input, &$value)
+    {
+        if (!self::readFixed32($input, $value)) {
+            return false;
+        }
+        if (PHP_INT_SIZE === 8) {
+            $value |= (-($value >> 31) << 32);
+        }
+        return true;
+    }
+
+    public static function readSfixed64(&$input, &$value)
+    {
+        if (!self::readFixed64($input, $value)) {
+            return false;
+        }
+        $value = Int64::newValue($value->high, $value->low);
+        return true;
+    }
+
+    public static function readFloat(&$input, &$value)
+    {
+        $data = null;
+        if (!$input->readRaw(4, $data)) {
+            return false;
+        }
+        $value = unpack('f', $data)[1];
+        return true;
+    }
+
+    public static function readDouble(&$input, &$value)
+    {
+        $data = null;
+        if (!$input->readRaw(8, $data)) {
+            return false;
+        }
+        $value = unpack('d', $data)[1];
+        return true;
+    }
+
+    public static function readBool(&$input, &$value)
+    {
+        if (!$input->readVarint64($value)) {
+            return false;
+        }
+        if ($value->high === 0 && $value->low === 0) {
+            $value = false;
+        } else {
+            $value = true;
+        }
+        return true;
+    }
+
+    public static function readString(&$input, &$value)
+    {
+        $length = 0;
+        return $input->readVarintSizeAsInt($length) && $input->readRaw($length, $value);
+    }
+
+    public static function readMessage(&$input, &$message)
+    {
+        $length = 0;
+        if (!$input->readVarintSizeAsInt($length)) {
+            return false;
+        }
+        $old_limit = 0;
+        $recursion_limit = 0;
+        $input->incrementRecursionDepthAndPushLimit(
+            $length,
+            $old_limit,
+            $recursion_limit);
+        if ($recursion_limit < 0 || !$message->parseFromStream($input)) {
+            return false;
+        }
+        return $input->decrementRecursionDepthAndPopLimit($old_limit);
+    }
+
+    public static function writeTag(&$output, $tag)
+    {
+        return $output->writeTag($tag);
+    }
+
+    public static function writeInt32(&$output, $value)
+    {
+        return $output->writeVarint32($value);
+    }
+
+    public static function writeInt64(&$output, $value)
+    {
+        return $output->writeVarint64($value);
+    }
+
+    public static function writeUint32(&$output, $value)
+    {
+        return $output->writeVarint32($value);
+    }
+
+    public static function writeUint64(&$output, $value)
+    {
+        return $output->writeVarint64($value);
+    }
+
+    public static function writeSint32(&$output, $value)
+    {
+        $value = GPBWire::zigZagEncode32($value);
+        return $output->writeVarint64($value);
+    }
+
+    public static function writeSint64(&$output, $value)
+    {
+        $value = GPBWire::zigZagEncode64(GPBUtil::Int64($value));
+        return $output->writeVarint64($value->toInteger());
+    }
+
+    public static function writeFixed32(&$output, $value)
+    {
+        return $output->writeLittleEndian32($value);
+    }
+
+    public static function writeFixed64(&$output, $value)
+    {
+        return $output->writeLittleEndian64($value);
+    }
+
+    public static function writeSfixed32(&$output, $value)
+    {
+        return $output->writeLittleEndian32($value);
+    }
+
+    public static function writeSfixed64(&$output, $value)
+    {
+        return $output->writeLittleEndian64($value);
+    }
+
+    public static function writeBool(&$output, $value)
+    {
+        if ($value) {
+            return $output->writeVarint32(1);
+        } else {
+            return $output->writeVarint32(0);
+        }
+    }
+
+    public static function writeFloat(&$output, $value)
+    {
+        $data = pack("f", $value);
+        return $output->writeRaw($data, 4);
+    }
+
+    public static function writeDouble(&$output, $value)
+    {
+        $data = pack("d", $value);
+        return $output->writeRaw($data, 8);
+    }
+
+    public static function writeString(&$output, $value)
+    {
+        return self::writeBytes($output, $value);
+    }
+
+    public static function writeBytes(&$output, $value)
+    {
+        $size = strlen($value);
+        if (!$output->writeVarint32($size)) {
+            return false;
+        }
+        return $output->writeRaw($value, $size);
+    }
+
+    public static function writeMessage(&$output, $value)
+    {
+        $size = $value->byteSize();
+        if (!$output->writeVarint32($size)) {
+            return false;
+        }
+        return $value->serializeToStream($output);
+    }
+
+    public static function makeTag($number, $type)
+    {
+        return ($number << 3) | self::getWireType($type);
+    }
+
+    public static function tagSize($field)
+    {
+        $tag = self::makeTag($field->getNumber(), $field->getType());
+        return self::varint32Size($tag);
+    }
+
+    public static function varint32Size($value)
+    {
+        if ($value < 0) {
+            return 5;
+        }
+        if ($value < (1 <<  7)) {
+            return 1;
+        }
+        if ($value < (1 << 14)) {
+            return 2;
+        }
+        if ($value < (1 << 21)) {
+            return 3;
+        }
+        if ($value < (1 << 28)) {
+            return 4;
+        }
+        return 5;
+    }
+
+    public static function sint32Size($value)
+    {
+        $value = self::zigZagEncode32($value);
+        return self::varint32Size($value);
+    }
+
+    public static function sint64Size($value)
+    {
+        $value = GPBUtil::Int64($value);
+        $value = self::zigZagEncode64($value);
+        return self::varint64Size($value->toInteger());
+    }
+
+    public static function varint64Size($value)
+    {
+        if ($value < 0) {
+            return 10;
+        }
+        if ($value < (1 <<  7)) {
+            return 1;
+        }
+        if ($value < (1 << 14)) {
+            return 2;
+        }
+        if ($value < (1 << 21)) {
+            return 3;
+        }
+        if ($value < (1 << 28)) {
+            return 4;
+        }
+        if ($value < (1 << 35)) {
+            return 5;
+        }
+        if ($value < (1 << 42)) {
+            return 6;
+        }
+        if ($value < (1 << 49)) {
+            return 7;
+        }
+        if ($value < (1 << 56)) {
+            return 8;
+        }
+        return 9;
+    }
+
+    public static function serializeFieldToStream(
+        $value,
+        $field,
+        $need_tag,
+        &$output)
+    {
+        if ($need_tag) {
+            if (!GPBWire::writeTag(
+                $output,
+                self::makeTag(
+                    $field->getNumber(),
+                    $field->getType()))) {
+                return false;
+            }
+        }
+        switch ($field->getType()) {
+            case GPBType::DOUBLE:
+                if (!GPBWire::writeDouble($output, $value)) {
+                    return false;
+                }
+                break;
+            case GPBType::FLOAT:
+                if (!GPBWire::writeFloat($output, $value)) {
+                    return false;
+                }
+                break;
+            case GPBType::INT64:
+                if (!GPBWire::writeInt64($output, $value)) {
+                    return false;
+                }
+                break;
+            case GPBType::UINT64:
+                if (!GPBWire::writeUint64($output, $value)) {
+                    return false;
+                }
+                break;
+            case GPBType::INT32:
+                if (!GPBWire::writeInt32($output, $value)) {
+                    return false;
+                }
+                break;
+            case GPBType::FIXED32:
+                if (!GPBWire::writeFixed32($output, $value)) {
+                    return false;
+                }
+                break;
+            case GPBType::FIXED64:
+                if (!GPBWire::writeFixed64($output, $value)) {
+                    return false;
+                }
+                break;
+            case GPBType::BOOL:
+                if (!GPBWire::writeBool($output, $value)) {
+                    return false;
+                }
+                break;
+            case GPBType::STRING:
+                if (!GPBWire::writeString($output, $value)) {
+                    return false;
+                }
+                break;
+            //    case GPBType::GROUP:
+            //      echo "GROUP\xA";
+            //      trigger_error("Not implemented.", E_ERROR);
+            //      break;
+            case GPBType::MESSAGE:
+                if (!GPBWire::writeMessage($output, $value)) {
+                    return false;
+                }
+                break;
+            case GPBType::BYTES:
+                if (!GPBWire::writeBytes($output, $value)) {
+                    return false;
+                }
+                break;
+            case GPBType::UINT32:
+                if (!GPBWire::writeUint32($output, $value)) {
+                    return false;
+                }
+                break;
+            case GPBType::ENUM:
+                if (!GPBWire::writeInt32($output, $value)) {
+                    return false;
+                }
+                break;
+            case GPBType::SFIXED32:
+                if (!GPBWire::writeSfixed32($output, $value)) {
+                    return false;
+                }
+                break;
+            case GPBType::SFIXED64:
+                if (!GPBWire::writeSfixed64($output, $value)) {
+                    return false;
+                }
+                break;
+            case GPBType::SINT32:
+                if (!GPBWire::writeSint32($output, $value)) {
+                    return false;
+                }
+                break;
+            case GPBType::SINT64:
+                if (!GPBWire::writeSint64($output, $value)) {
+                    return false;
+                }
+                break;
+            default:
+                user_error("Unsupported type.");
+                return false;
+        }
+
+        return true;
+    }
+}
diff --git a/php/src/Google/Protobuf/Internal/InputStream.php b/php/src/Google/Protobuf/Internal/InputStream.php
new file mode 100644
index 0000000..18d0707
--- /dev/null
+++ b/php/src/Google/Protobuf/Internal/InputStream.php
@@ -0,0 +1,323 @@
+<?php
+
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+namespace Google\Protobuf\Internal;
+
+use Google\Protobuf\Internal\Uint64;
+
+class InputStream
+{
+
+    private $buffer;
+    private $buffer_size_after_limit;
+    private $buffer_end;
+    private $current;
+    private $current_limit;
+    private $legitimate_message_end;
+    private $recursion_budget;
+    private $recursion_limit;
+    private $total_bytes_limit;
+    private $total_bytes_read;
+
+    const MAX_VARINT_BYTES = 10;
+    const MAX_VARINT32_BYTES = 5;
+    const DEFAULT_RECURSION_LIMIT = 100;
+    const DEFAULT_TOTAL_BYTES_LIMIT = 33554432; // 32 << 20, 32MB
+
+    public function __construct($buffer)
+    {
+        $start = 0;
+        $end = strlen($buffer);
+        $this->buffer = $buffer;
+        $this->buffer_size_after_limit = 0;
+        $this->buffer_end = $end;
+        $this->current = $start;
+        $this->current_limit = $end;
+        $this->legitimate_message_end = false;
+        $this->recursion_budget = self::DEFAULT_RECURSION_LIMIT;
+        $this->recursion_limit = self::DEFAULT_RECURSION_LIMIT;
+        $this->total_bytes_limit = self::DEFAULT_TOTAL_BYTES_LIMIT;
+        $this->total_bytes_read = $end - $start;
+    }
+
+    private function advance($amount)
+    {
+        $this->current += $amount;
+    }
+
+    private function bufferSize()
+    {
+        return $this->buffer_end - $this->current;
+    }
+
+    private function current()
+    {
+        return $this->total_bytes_read -
+            ($this->buffer_end - $this->current +
+            $this->buffer_size_after_limit);
+    }
+
+    private function recomputeBufferLimits()
+    {
+        $this->buffer_end += $this->buffer_size_after_limit;
+        $closest_limit = min($this->current_limit, $this->total_bytes_limit);
+        if ($closest_limit < $this->total_bytes_read) {
+            // The limit position is in the current buffer.  We must adjust the
+            // buffer size accordingly.
+            $this->buffer_size_after_limit = $this->total_bytes_read -
+                $closest_limit;
+            $this->buffer_end -= $this->buffer_size_after_limit;
+        } else {
+            $this->buffer_size_after_limit = 0;
+        }
+    }
+
+    private function consumedEntireMessage()
+    {
+        return $this->legitimate_message_end;
+    }
+
+    /**
+     * Read uint32 into $var. Advance buffer with consumed bytes. If the
+     * contained varint is larger than 32 bits, discard the high order bits.
+     * @param $var.
+     */
+    public function readVarint32(&$var)
+    {
+        if (!$this->readVarint64($var)) {
+            return false;
+        }
+        $var = $var->toInteger() & 0xFFFFFFFF;
+        // Convert large uint32 to int32.
+        if (PHP_INT_SIZE === 8 && ($var > 0x7FFFFFFF)) {
+            $var = $var | (0xFFFFFFFF << 32);
+        }
+        return true;
+    }
+
+    /**
+     * Read Uint64 into $var. Advance buffer with consumed bytes.
+     * @param $var.
+     */
+    public function readVarint64(&$var)
+    {
+        $result = new Uint64(0);
+        $count = 0;
+        $b = 0;
+
+        do {
+            if ($this->current === $this->buffer_end) {
+                return false;
+            }
+            if ($count === self::MAX_VARINT_BYTES) {
+                return false;
+            }
+            $b = ord($this->buffer[$this->current]);
+            $result->bitOr((new Uint64($b & 0x7F))->leftShift(7 * $count));
+            $this->advance(1);
+            $count += 1;
+        } while ($b & 0x80);
+
+        $var = $result;
+        return true;
+    }
+
+    /**
+     * Read int into $var. If the result is larger than the largest integer, $var
+     * will be -1. Advance buffer with consumed bytes.
+     * @param $var.
+     */
+    public function readVarintSizeAsInt(&$var)
+    {
+        if (!$this->readVarint64($var)) {
+            return false;
+        }
+        $var = $var->toInteger();
+        return true;
+    }
+
+    /**
+     * Read 32-bit unsiged integer to $var. If the buffer has less than 4 bytes,
+     * return false. Advance buffer with consumed bytes.
+     * @param $var.
+     */
+    public function readLittleEndian32(&$var)
+    {
+        $data = null;
+        if (!$this->readRaw(4, $data)) {
+            return false;
+        }
+        $var = unpack('V', $data);
+        $var = $var[1];
+        return true;
+    }
+
+    /**
+     * Read 64-bit unsiged integer to $var. If the buffer has less than 8 bytes,
+     * return false. Advance buffer with consumed bytes.
+     * @param $var.
+     */
+    public function readLittleEndian64(&$var)
+    {
+        $data = null;
+        if (!$this->readRaw(4, $data)) {
+            return false;
+        }
+        $low = unpack('V', $data)[1];
+        if (!$this->readRaw(4, $data)) {
+            return false;
+        }
+        $high = unpack('V', $data)[1];
+        $var = Uint64::newValue($high, $low);
+        return true;
+    }
+
+    /**
+     * Read tag into $var. Advance buffer with consumed bytes.
+     * @param $var.
+     */
+    public function readTag()
+    {
+        if ($this->current === $this->buffer_end) {
+            // Make sure that it failed due to EOF, not because we hit
+            // total_bytes_limit, which, unlike normal limits, is not a valid
+            // place to end a message.
+            $current_position = $this->total_bytes_read -
+                $this->buffer_size_after_limit;
+            if ($current_position >= $this->total_bytes_limit) {
+                // Hit total_bytes_limit_.  But if we also hit the normal limit,
+                // we're still OK.
+                $this->legitimate_message_end =
+                    ($this->current_limit === $this->total_bytes_limit);
+            } else {
+                $this->legitimate_message_end = true;
+            }
+            return 0;
+        }
+
+        $result = 0;
+        // The larget tag is 2^29 - 1, which can be represented by int32.
+        $success = $this->readVarint32($result);
+        if ($success) {
+            return $result;
+        } else {
+            return 0;
+        }
+    }
+
+    public function readRaw($size, &$buffer)
+    {
+        $current_buffer_size = 0;
+        if ($this->bufferSize() < $size) {
+            return false;
+        }
+
+        $buffer = substr($this->buffer, $this->current, $size);
+        $this->advance($size);
+
+        return true;
+    }
+
+    /* Places a limit on the number of bytes that the stream may read, starting
+     * from the current position.  Once the stream hits this limit, it will act
+     * like the end of the input has been reached until popLimit() is called.
+     *
+     * As the names imply, the stream conceptually has a stack of limits.  The
+     * shortest limit on the stack is always enforced, even if it is not the top
+     * limit.
+     *
+     * The value returned by pushLimit() is opaque to the caller, and must be
+     * passed unchanged to the corresponding call to popLimit().
+     *
+     * @param integer $byte_limit
+     */
+    public function pushLimit($byte_limit)
+    {
+        // Current position relative to the beginning of the stream.
+        $current_position = $this->current();
+        $old_limit = $this->current_limit;
+
+        // security: byte_limit is possibly evil, so check for negative values
+        // and overflow.
+        if ($byte_limit >= 0 && $byte_limit <= PHP_INT_MAX - $current_position) {
+            $this->current_limit = $current_position + $byte_limit;
+        } else {
+            // Negative or overflow.
+            $this->current_limit = PHP_INT_MAX;
+        }
+
+        // We need to enforce all limits, not just the new one, so if the previous
+        // limit was before the new requested limit, we continue to enforce the
+        // previous limit.
+        $this->current_limit = min($this->current_limit, $old_limit);
+
+        $this->recomputeBufferLimits();
+        return $old_limit;
+    }
+
+    /* The limit passed in is actually the *old* limit, which we returned from
+     * PushLimit().
+     *
+     * @param integer $byte_limit
+     */
+    public function popLimit($byte_limit)
+    {
+        $this->current_limit = $byte_limit;
+        $this->recomputeBufferLimits();
+        // We may no longer be at a legitimate message end.  ReadTag() needs to
+        // be called again to find out.
+        $this->legitimate_message_end = false;
+    }
+
+    public function incrementRecursionDepthAndPushLimit(
+    $byte_limit, &$old_limit, &$recursion_budget)
+    {
+        $old_limit = $this->pushLimit($byte_limit);
+        $recursion_limit = --$this->recursion_limit;
+    }
+
+    public function decrementRecursionDepthAndPopLimit($byte_limit)
+    {
+        $result = $this->consumedEntireMessage();
+        $this->popLimit($byte_limit);
+        ++$this->recursion_budget;
+        return $result;
+    }
+
+    public function bytesUntilLimit()
+    {
+        if ($this->current_limit === PHP_INT_MAX) {
+            return -1;
+        }
+        return $this->current_limit - $this->current;
+    }
+}
diff --git a/php/src/Google/Protobuf/Internal/MapEntry.php b/php/src/Google/Protobuf/Internal/MapEntry.php
new file mode 100644
index 0000000..926645e
--- /dev/null
+++ b/php/src/Google/Protobuf/Internal/MapEntry.php
@@ -0,0 +1,57 @@
+<?php
+
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+namespace Google\Protobuf\Internal;
+
+use Google\Protobuf\Internal\Message;
+
+class MapEntry extends Message
+{
+    public $key;
+    public $value;
+
+    public function setKey(&$key) {
+      $this->key = $key;
+    }
+
+    public function getKey() {
+      return $this->key;
+    }
+
+    public function setValue(&$value) {
+      $this->value = $value;
+    }
+
+    public function getValue() {
+      return $this->value;
+    }
+}
diff --git a/php/src/Google/Protobuf/Internal/MapField.php b/php/src/Google/Protobuf/Internal/MapField.php
new file mode 100644
index 0000000..14ee7eb
--- /dev/null
+++ b/php/src/Google/Protobuf/Internal/MapField.php
@@ -0,0 +1,321 @@
+<?php
+
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/**
+ * MapField and MapFieldIter are used by generated protocol message classes to
+ * manipulate map fields.
+ */
+
+namespace Google\Protobuf\Internal;
+
+/**
+ * MapFieldIter is used to iterate MapField. It is also need for the foreach
+ * syntax.
+ */
+class MapFieldIter implements \Iterator
+{
+
+    /**
+     * @ignore
+     */
+    private $container;
+
+    /**
+     * Create iterator instance for MapField.
+     *
+     * @param MapField The MapField instance for which this iterator is
+     * created.
+     * @ignore
+     */
+    public function __construct($container)
+    {
+        $this->container = $container;
+    }
+
+    /**
+     * Reset the status of the iterator
+     *
+     * @return void
+     */
+    public function rewind()
+    {
+        return reset($this->container);
+    }
+
+    /**
+     * Return the element at the current position.
+     *
+     * @return object The element at the current position.
+     */
+    public function current()
+    {
+        return current($this->container);
+    }
+
+    /**
+     * Return the current key.
+     *
+     * @return object The current key.
+     */
+    public function key()
+    {
+        return key($this->container);
+    }
+
+    /**
+     * Move to the next position.
+     *
+     * @return void
+     */
+    public function next()
+    {
+        return next($this->container);
+    }
+
+    /**
+     * Check whether there are more elements to iterate.
+     *
+     * @return bool True if there are more elements to iterate.
+     */
+    public function valid()
+    {
+        return key($this->container) !== null;
+    }
+}
+
+/**
+ * @ignore
+ */
+function checkKey($key_type, &$key)
+{
+    switch ($key_type) {
+        case GPBType::INT32:
+            GPBUtil::checkInt32($key);
+            break;
+        case GPBType::UINT32:
+            GPBUtil::checkUint32($key);
+            break;
+        case GPBType::INT64:
+            GPBUtil::checkInt64($key);
+            break;
+        case GPBType::UINT64:
+            GPBUtil::checkUint64($key);
+            break;
+        case GPBType::FIXED64:
+            GPBUtil::checkUint64($key);
+            break;
+        case GPBType::FIXED32:
+            GPBUtil::checkUint32($key);
+            break;
+        case GPBType::SFIXED64:
+            GPBUtil::checkInt64($key);
+            break;
+        case GPBType::SFIXED32:
+            GPBUtil::checkInt32($key);
+            break;
+        case GPBType::SINT64:
+            GPBUtil::checkInt64($key);
+            break;
+        case GPBType::SINT32:
+            GPBUtil::checkInt32($key);
+            break;
+        case GPBType::BOOL:
+            GPBUtil::checkBool($key);
+            break;
+        case GPBType::STRING:
+            GPBUtil::checkString($key, true);
+            break;
+        default:
+            var_dump($key_type);
+            trigger_error(
+                "Given type cannot be map key.",
+                E_USER_ERROR);
+            break;
+    }
+}
+
+/**
+ * MapField is used by generated protocol message classes to manipulate map
+ * fields. It can be used like native PHP array.
+ */
+class MapField implements \ArrayAccess, \IteratorAggregate, \Countable
+{
+    /**
+     * @ignore
+     */
+    private $container;
+    /**
+     * @ignore
+     */
+    private $key_type;
+    /**
+     * @ignore
+     */
+    private $value_type;
+    /**
+     * @ignore
+     */
+    private $value_klass;
+
+    /**
+     * Constructs an instance of MapField.
+     *
+     * @param long $key_type Type of the stored key element.
+     * @param long $value_type Type of the stored value element.
+     * @param string $klass Message/Enum class name of value instance
+     * (message/enum fields only).
+     * @ignore
+     */
+    public function __construct($key_type, $value_type, $klass = null)
+    {
+        $this->container = [];
+        $this->key_type = $key_type;
+        $this->value_type = $value_type;
+        $this->klass = $klass;
+    }
+
+    /**
+     * Return the element at the given key.
+     *
+     * This will also be called for: $ele = $arr[$key]
+     *
+     * @param object $key The key of the element to be fetched.
+     * @return object The stored element at given key.
+     * @throws ErrorException Invalid type for index.
+     * @throws ErrorException Non-existing index.
+     */
+    public function offsetGet($key)
+    {
+        return $this->container[$key];
+    }
+
+    /**
+     * Assign the element at the given key.
+     *
+     * This will also be called for: $arr[$key] = $value
+     *
+     * @param object $key The key of the element to be fetched.
+     * @param object $value The element to be assigned.
+     * @return void
+     * @throws ErrorException Invalid type for key.
+     * @throws ErrorException Invalid type for value.
+     * @throws ErrorException Non-existing key.
+     */
+    public function offsetSet($key, $value)
+    {
+        checkKey($this->key_type, $key);
+
+        switch ($this->value_type) {
+            case GPBType::INT32:
+                GPBUtil::checkInt32($value);
+                break;
+            case GPBType::UINT32:
+                GPBUtil::checkUint32($value);
+                break;
+            case GPBType::INT64:
+                GPBUtil::checkInt64($value);
+                break;
+            case GPBType::UINT64:
+                GPBUtil::checkUint64($value);
+                break;
+            case GPBType::FLOAT:
+                GPBUtil::checkFloat($value);
+                break;
+            case GPBType::DOUBLE:
+                GPBUtil::checkDouble($value);
+                break;
+            case GPBType::BOOL:
+                GPBUtil::checkBool($value);
+                break;
+            case GPBType::STRING:
+                GPBUtil::checkString($value, true);
+                break;
+            case GPBType::MESSAGE:
+                GPBUtil::checkMessage($value, $this->klass);
+                break;
+            default:
+                break;
+        }
+
+        $this->container[$key] = $value;
+    }
+
+    /**
+     * Remove the element at the given key.
+     *
+     * This will also be called for: unset($arr)
+     *
+     * @param object $key The key of the element to be removed.
+     * @return void
+     * @throws ErrorException Invalid type for key.
+     */
+    public function offsetUnset($key)
+    {
+        checkKey($this->key_type, $key);
+        unset($this->container[$key]);
+    }
+
+    /**
+     * Check the existence of the element at the given key.
+     *
+     * This will also be called for: isset($arr)
+     *
+     * @param object $key The key of the element to be removed.
+     * @return bool True if the element at the given key exists.
+     * @throws ErrorException Invalid type for key.
+     */
+    public function offsetExists($key)
+    {
+        checkKey($this->key_type, $key);
+        return isset($this->container[$key]);
+    }
+
+    /**
+     * @ignore
+     */
+    public function getIterator()
+    {
+        return new MapFieldIter($this->container);
+    }
+
+    /**
+     * Return the number of stored elements.
+     *
+     * This will also be called for: count($arr)
+     *
+     * @return integer The number of stored elements.
+     */
+    public function count()
+    {
+        return count($this->container);
+    }
+}
diff --git a/php/src/Google/Protobuf/Internal/Message.php b/php/src/Google/Protobuf/Internal/Message.php
new file mode 100644
index 0000000..a8de6a1
--- /dev/null
+++ b/php/src/Google/Protobuf/Internal/Message.php
@@ -0,0 +1,671 @@
+<?php
+
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/**
+ * Defines Message, the parent class extended by all protocol message classes.
+ */
+
+namespace Google\Protobuf\Internal;
+
+use Google\Protobuf\Internal\InputStream;
+use Google\Protobuf\Internal\OutputStream;
+use Google\Protobuf\Internal\DescriptorPool;
+use Google\Protobuf\Internal\GPBLabel;
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\GPBWire;
+use Google\Protobuf\Internal\MapEntry;
+use Google\Protobuf\Internal\RepeatedField;
+
+/**
+ * Parent class of all proto messages. Users should not instantiate this class
+ * or extend this class or its child classes by their own.  See the comment of
+ * specific functions for more details.
+ */
+class Message
+{
+
+    /**
+     * @ignore
+     */
+    private $desc;
+
+    /**
+     * @ignore
+     */
+    public function __construct($desc = NULL)
+    {
+        // MapEntry message is shared by all types of map fields, whose
+        // descriptors are different from each other. Thus, we cannot find a
+        // specific descriptor from the descriptor pool.
+        if (get_class($this) === 'Google\Protobuf\Internal\MapEntry') {
+            $this->desc = $desc;
+            return;
+        }
+        $pool = DescriptorPool::getGeneratedPool();
+        $this->desc = $pool->getDescriptorByClassName(get_class($this));
+        foreach ($this->desc->getField() as $field) {
+            $setter = $field->getSetter();
+            if ($field->isMap()) {
+                $message_type = $field->getMessageType();
+                $key_field = $message_type->getFieldByNumber(1);
+                $value_field = $message_type->getFieldByNumber(2);
+                switch ($value_field->getType()) {
+                    case GPBType::MESSAGE:
+                    case GPBType::GROUP:
+                        $this->$setter(
+                            new MapField(
+                                $key_field->getType(),
+                                $value_field->getType(),
+                                $value_field->getMessageType()->getClass()));
+                        break;
+                    case GPBType::ENUM:
+                        $this->$setter(
+                            new MapField(
+                                $key_field->getType(),
+                                $value_field->getType(),
+                                $value_field->getEnumType()->getClass()));
+                        break;
+                    default:
+                        $this->$setter(new MapField($key_field->getType(),
+                                                    $value_field->getType()));
+                        break;
+                }
+            } else if ($field->getLabel() === GPBLabel::REPEATED) {
+                switch ($field->getType()) {
+                    case GPBType::MESSAGE:
+                    case GPBType::GROUP:
+                        $this->$setter(
+                            new RepeatedField(
+                                $field->getType(),
+                                $field->getMessageType()->getClass()));
+                        break;
+                    case GPBType::ENUM:
+                        $this->$setter(
+                            new RepeatedField(
+                                $field->getType(),
+                                $field->getEnumType()->getClass()));
+                        break;
+                    default:
+                        $this->$setter(new RepeatedField($field->getType()));
+                        break;
+                }
+            } else if ($field->getOneofIndex() !== -1) {
+                $oneof = $this->desc->getOneofDecl()[$field->getOneofIndex()];
+                $oneof_name = $oneof->getName();
+                $this->$oneof_name = new OneofField($oneof);
+            }
+        }
+    }
+
+    protected function readOneof($number)
+    {
+        $field = $this->desc->getFieldByNumber($number);
+        $oneof = $this->desc->getOneofDecl()[$field->getOneofIndex()];
+        $oneof_name = $oneof->getName();
+        $oneof_field = $this->$oneof_name;
+        if ($number === $oneof_field->getNumber()) {
+            return $oneof_field->getValue();
+        } else {
+            return $this->defaultValue($field);
+        }
+    }
+
+    protected function writeOneof($number, $value)
+    {
+        $field = $this->desc->getFieldByNumber($number);
+        $oneof = $this->desc->getOneofDecl()[$field->getOneofIndex()];
+        $oneof_name = $oneof->getName();
+        $oneof_field = $this->$oneof_name;
+        $oneof_field->setValue($value);
+        $oneof_field->setFieldName($field->getName());
+        $oneof_field->setNumber($number);
+    }
+
+    /**
+     * @ignore
+     */
+    private function defaultValue($field)
+    {
+        $value = null;
+
+        switch ($field->getType()) {
+            case GPBType::DOUBLE:
+            case GPBType::FLOAT:
+                return 0.0;
+            case GPBType::UINT32:
+            case GPBType::UINT64:
+            case GPBType::INT32:
+            case GPBType::INT64:
+            case GPBType::FIXED32:
+            case GPBType::FIXED64:
+            case GPBType::SFIXED32:
+            case GPBType::SFIXED64:
+            case GPBType::SINT32:
+            case GPBType::SINT64:
+            case GPBType::ENUM:
+                return 0;
+            case GPBType::BOOL:
+                return false;
+            case GPBType::STRING:
+            case GPBType::BYTES:
+                return "";
+            case GPBType::GROUP:
+            case GPBType::MESSAGE:
+                return null;
+            default:
+                user_error("Unsupported type.");
+                return false;
+        }
+    }
+
+    /**
+     * @ignore
+     */
+    private static function parseFieldFromStreamNoTag($input, $field, &$value)
+    {
+        switch ($field->getType()) {
+            case GPBType::DOUBLE:
+                if (!GPBWire::readDouble($input, $value)) {
+                    return false;
+                }
+                break;
+            case GPBType::FLOAT:
+                if (!GPBWire::readFloat($input, $value)) {
+                    return false;
+                }
+                break;
+            case GPBType::INT64:
+                if (!GPBWire::readInt64($input, $value)) {
+                    return false;
+                }
+                $value = $value->toInteger();
+                break;
+            case GPBType::UINT64:
+                if (!GPBWire::readUint64($input, $value)) {
+                    return false;
+                }
+                $value = $value->toInteger();
+                break;
+            case GPBType::INT32:
+                if (!GPBWire::readInt32($input, $value)) {
+                    return false;
+                }
+                break;
+            case GPBType::FIXED64:
+                if (!GPBWire::readFixed64($input, $value)) {
+                    return false;
+                }
+                $value = $value->toInteger();
+                break;
+            case GPBType::FIXED32:
+                if (!GPBWire::readFixed32($input, $value)) {
+                    return false;
+                }
+                break;
+            case GPBType::BOOL:
+                if (!GPBWire::readBool($input, $value)) {
+                    return false;
+                }
+                break;
+            case GPBType::STRING:
+                // TODO(teboring): Add utf-8 check.
+                if (!GPBWire::readString($input, $value)) {
+                    return false;
+                }
+                break;
+            case GPBType::GROUP:
+                echo "GROUP\xA";
+                trigger_error("Not implemented.", E_ERROR);
+                break;
+            case GPBType::MESSAGE:
+                if ($field->isMap()) {
+                    $value = new MapEntry($field->getMessageType());
+                } else {
+                    $klass = $field->getMessageType()->getClass();
+                    $value = new $klass;
+                }
+                if (!GPBWire::readMessage($input, $value)) {
+                    return false;
+                }
+                break;
+            case GPBType::BYTES:
+                if (!GPBWire::readString($input, $value)) {
+                    return false;
+                }
+                break;
+            case GPBType::UINT32:
+                if (!GPBWire::readUint32($input, $value)) {
+                    return false;
+                }
+                break;
+            case GPBType::ENUM:
+                // TODO(teboring): Check unknown enum value.
+                if (!GPBWire::readInt32($input, $value)) {
+                    return false;
+                }
+                break;
+            case GPBType::SFIXED32:
+                if (!GPBWire::readSfixed32($input, $value)) {
+                    return false;
+                }
+                break;
+            case GPBType::SFIXED64:
+                if (!GPBWire::readSfixed64($input, $value)) {
+                    return false;
+                }
+                $value = $value->toInteger();
+                break;
+            case GPBType::SINT32:
+                if (!GPBWire::readSint32($input, $value)) {
+                    return false;
+                }
+                break;
+            case GPBType::SINT64:
+                if (!GPBWire::readSint64($input, $value)) {
+                    return false;
+                }
+                $value = $value->toInteger();
+                break;
+            default:
+                user_error("Unsupported type.");
+                return false;
+        }
+        return true;
+    }
+
+    /**
+     * @ignore
+     */
+    private function parseFieldFromStream($tag, $input, $field)
+    {
+        $value = null;
+        $field_type = $field->getType();
+
+        $value_format = GPBWire::UNKNOWN;
+        if (GPBWire::getTagWireType($tag) ===
+            GPBWire::getWireType($field_type)) {
+            $value_format = GPBWire::NORMAL_FORMAT;
+        } elseif ($field->isPackable() &&
+            GPBWire::getTagWireType($tag) ===
+            GPBWire::WIRETYPE_LENGTH_DELIMITED) {
+            $value_format = GPBWire::PACKED_FORMAT;
+        }
+
+        if ($value_format === GPBWire::NORMAL_FORMAT) {
+            if (!self::parseFieldFromStreamNoTag($input, $field, $value)) {
+                return false;
+            }
+        } elseif ($value_format === GPBWire::PACKED_FORMAT) {
+            $length = 0;
+            if (!GPBWire::readInt32($input, $length)) {
+                return false;
+            }
+            $limit = $input->pushLimit($length);
+            $getter = $field->getGetter();
+            while ($input->bytesUntilLimit() > 0) {
+                if (!self::parseFieldFromStreamNoTag($input, $field, $value)) {
+                    return false;
+                }
+                $this->$getter()[] = $value;
+            }
+            $input->popLimit($limit);
+            return true;
+        } else {
+            return false;
+        }
+
+        if ($field->isMap()) {
+            $getter = $field->getGetter();
+            $this->$getter()[$value->getKey()] = $value->getValue();
+        } else if ($field->isRepeated()) {
+            $getter = $field->getGetter();
+            $this->$getter()[] = $value;
+        } else {
+            $setter = $field->getSetter();
+            $this->$setter($value);
+        }
+
+        return true;
+    }
+
+    /**
+     * Parses a protocol buffer contained in a string.
+     *
+     * This function takes a string in the (non-human-readable) binary wire
+     * format, matching the encoding output by encode().
+     *
+     * @param string $data Binary protobuf data.
+     * @return bool Return true on success.
+     */
+    public function decode($data)
+    {
+        $input = new InputStream($data);
+        $this->parseFromStream($input);
+    }
+
+    /**
+     * @ignore
+     */
+    public function parseFromStream($input)
+    {
+        while (true) {
+            $tag = $input->readTag();
+            // End of input.  This is a valid place to end, so return true.
+            if ($tag === 0) {
+                return true;
+            }
+
+            $number = GPBWire::getTagFieldNumber($tag);
+            $field = $this->desc->getFieldByNumber($number);
+
+            if (!$this->parseFieldFromStream($tag, $input, $field)) {
+                return false;
+            }
+        }
+    }
+
+    /**
+     * @ignore
+     */
+    private function serializeSingularFieldToStream($field, &$output)
+    {
+        if (!$this->existField($field)) {
+            return true;
+        }
+        $getter = $field->getGetter();
+        $value = $this->$getter();
+        if (!GPBWire::serializeFieldToStream($value, $field, true, $output)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * @ignore
+     */
+    private function serializeRepeatedFieldToStream($field, &$output)
+    {
+        $getter = $field->getGetter();
+        $values = $this->$getter();
+        $count = count($values);
+        if ($count === 0) {
+            return true;
+        }
+
+        $packed = $field->getPacked();
+        if ($packed) {
+            if (!GPBWire::writeTag(
+                $output,
+                GPBWire::makeTag($field->getNumber(), GPBType::STRING))) {
+                return false;
+            }
+            $size = 0;
+            foreach ($values as $value) {
+                $size += $this->fieldDataOnlyByteSize($field, $value);
+            }
+            if (!$output->writeVarint32($size)) {
+                return false;
+            }
+        }
+
+        foreach ($values as $value) {
+            if (!GPBWire::serializeFieldToStream(
+                $value,
+                $field,
+                !$packed,
+                $output)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * @ignore
+     */
+    private function serializeMapFieldToStream($field, $output)
+    {
+        $getter = $field->getGetter();
+        $values = $this->$getter();
+        $count = count($values);
+        if ($count === 0) {
+            return true;
+        }
+
+        foreach ($values as $key => $value) {
+            $map_entry = new MapEntry($field->getMessageType());
+            $map_entry->setKey($key);
+            $map_entry->setValue($value);
+            if (!GPBWire::serializeFieldToStream(
+                $map_entry,
+                $field,
+                true,
+                $output)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * @ignore
+     */
+    private function serializeFieldToStream(&$output, $field)
+    {
+        if ($field->isMap()) {
+            return $this->serializeMapFieldToStream($field, $output);
+        } elseif ($field->isRepeated()) {
+            return $this->serializeRepeatedFieldToStream($field, $output);
+        } else {
+            return $this->serializeSingularFieldToStream($field, $output);
+        }
+    }
+
+    /**
+     * @ignore
+     */
+    public function serializeToStream(&$output)
+    {
+        $fields = $this->desc->getField();
+        foreach ($fields as $field) {
+            if (!$this->serializeFieldToStream($output, $field)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Serialize the message to string.
+     * @return string Serialized binary protobuf data.
+     */
+    public function encode()
+    {
+        $output = new OutputStream($this->byteSize());
+        $this->serializeToStream($output);
+        return $output->getData();
+    }
+
+    /**
+     * @ignore
+     */
+    private function existField($field)
+    {
+        $getter = $field->getGetter();
+        $value = $this->$getter();
+        return $value !== $this->defaultValue($field);
+    }
+
+    /**
+     * @ignore
+     */
+    private function repeatedFieldDataOnlyByteSize($field)
+    {
+        $size = 0;
+
+        $getter = $field->getGetter();
+        $values = $this->$getter();
+        $count = count($values);
+        if ($count !== 0) {
+            $size += $count * GPBWire::tagSize($field);
+            foreach ($values as $value) {
+                $size += $this->singularFieldDataOnlyByteSize($field);
+            }
+        }
+    }
+
+    /**
+     * @ignore
+     */
+    private function fieldDataOnlyByteSize($field, $value)
+    {
+        $size = 0;
+
+        switch ($field->getType()) {
+            case GPBType::BOOL:
+                $size += 1;
+                break;
+            case GPBType::FLOAT:
+            case GPBType::FIXED32:
+            case GPBType::SFIXED32:
+                $size += 4;
+                break;
+            case GPBType::DOUBLE:
+            case GPBType::FIXED64:
+            case GPBType::SFIXED64:
+                $size += 8;
+                break;
+            case GPBType::UINT32:
+            case GPBType::INT32:
+            case GPBType::ENUM:
+                $size += GPBWire::varint32Size($value);
+                break;
+            case GPBType::UINT64:
+            case GPBType::INT64:
+                $size += GPBWire::varint64Size($value);
+                break;
+            case GPBType::SINT32:
+                $size += GPBWire::sint32Size($value);
+                break;
+            case GPBType::SINT64:
+                $size += GPBWire::sint64Size($value);
+                break;
+            case GPBType::STRING:
+            case GPBType::BYTES:
+                $size += strlen($value);
+                $size += GPBWire::varint32Size($size);
+                break;
+            case GPBType::MESSAGE:
+                $size += $value->byteSize();
+                $size += GPBWire::varint32Size($size);
+                break;
+            case GPBType::GROUP:
+                // TODO(teboring): Add support.
+                user_error("Unsupported type.");
+                break;
+            default:
+                user_error("Unsupported type.");
+                return 0;
+        }
+
+        return $size;
+    }
+
+    /**
+     * @ignore
+     */
+    private function fieldByteSize($field)
+    {
+        $size = 0;
+        if ($field->isMap()) {
+            $getter = $field->getGetter();
+            $values = $this->$getter();
+            $count = count($values);
+            if ($count !== 0) {
+                $size += $count * GPBWire::tagSize($field);
+                $message_type = $field->getMessageType();
+                $key_field = $message_type->getFieldByNumber(1);
+                $value_field = $message_type->getFieldByNumber(2);
+                foreach ($values as $key => $value) {
+                    $data_size = 0;
+                    $data_size += $this->fieldDataOnlyByteSize($key_field, $key);
+                    $data_size += $this->fieldDataOnlyByteSize(
+                        $value_field,
+                        $value);
+                    $data_size += GPBWire::tagSize($key_field);
+                    $data_size += GPBWire::tagSize($value_field);
+                    $size += GPBWire::varint32Size($data_size) + $data_size;
+                }
+            }
+        } elseif ($field->isRepeated()) {
+            $getter = $field->getGetter();
+            $values = $this->$getter();
+            $count = count($values);
+            if ($count !== 0) {
+                if ($field->getPacked()) {
+                    $data_size = 0;
+                    foreach ($values as $value) {
+                        $data_size += $this->fieldDataOnlyByteSize($field, $value);
+                    }
+                    $size += GPBWire::tagSize($field);
+                    $size += GPBWire::varint32Size($data_size);
+                    $size += $data_size;
+                } else {
+                    $size += $count * GPBWire::tagSize($field);
+                    foreach ($values as $value) {
+                        $size += $this->fieldDataOnlyByteSize($field, $value);
+                    }
+                }
+            }
+        } elseif ($this->existField($field)) {
+            $size += GPBWire::tagSize($field);
+            $getter = $field->getGetter();
+            $value = $this->$getter();
+            $size += $this->fieldDataOnlyByteSize($field, $value);
+        }
+        return $size;
+    }
+
+    /**
+     * @ignore
+     */
+    public function byteSize()
+    {
+        $size = 0;
+
+        $fields = $this->desc->getField();
+        foreach ($fields as $field) {
+            $size += $this->fieldByteSize($field);
+        }
+        return $size;
+    }
+}
diff --git a/php/src/Google/Protobuf/Internal/MessageBuilderContext.php b/php/src/Google/Protobuf/Internal/MessageBuilderContext.php
new file mode 100644
index 0000000..2724d26
--- /dev/null
+++ b/php/src/Google/Protobuf/Internal/MessageBuilderContext.php
@@ -0,0 +1,120 @@
+<?php
+
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+namespace Google\Protobuf\Internal;
+
+use Google\Protobuf\Internal\GPBLabel;
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\Descriptor;
+use Google\Protobuf\Internal\FieldDescriptor;
+
+class MessageBuilderContext
+{
+
+    private $descriptor;
+    private $pool;
+
+    public function __construct($full_name, $klass, $pool)
+    {
+        $this->descriptor = new Descriptor();
+        $this->descriptor->setFullName($full_name);
+        $this->descriptor->setClass($klass);
+        $this->pool = $pool;
+    }
+
+    private function getFieldDescriptor($name, $label, $type,
+                                      $number, $type_name = null)
+    {
+        $field = new FieldDescriptor();
+        $field->setName($name);
+        $camel_name = implode('', array_map('ucwords', explode('_', $name)));
+        $field->setGetter('get' . $camel_name);
+        $field->setSetter('set' . $camel_name);
+        $field->setType($type);
+        $field->setNumber($number);
+        $field->setLabel($label);
+
+        // At this time, the message/enum type may have not been added to pool.
+        // So we use the type name as place holder and will replace it with the
+        // actual descriptor in cross building.
+        switch ($type) {
+        case GPBType::MESSAGE:
+          $field->setMessageType($type_name);
+          break;
+        case GPBType::ENUM:
+          $field->setEnumType($type_name);
+          break;
+        default:
+          break;
+        }
+
+        return $field;
+    }
+
+    public function optional($name, $type, $number, $type_name = null)
+    {
+        $this->descriptor->addField($this->getFieldDescriptor(
+            $name,
+            GPBLabel::OPTIONAL,
+            $type,
+            $number,
+            $type_name));
+        return $this;
+    }
+
+    public function repeated($name, $type, $number, $type_name = null)
+    {
+        $this->descriptor->addField($this->getFieldDescriptor(
+            $name,
+            GPBLabel::REPEATED,
+            $type,
+            $number,
+            $type_name));
+        return $this;
+    }
+
+    public function required($name, $type, $number, $type_name = null)
+    {
+        $this->descriptor->addField($this->getFieldDescriptor(
+            $name,
+            GPBLabel::REQUIRED,
+            $type,
+            $number,
+            $type_name));
+        return $this;
+    }
+
+    public function finalizeToPool()
+    {
+        $this->pool->addDescriptor($this->descriptor);
+    }
+}
diff --git a/php/src/Google/Protobuf/Internal/OneofField.php b/php/src/Google/Protobuf/Internal/OneofField.php
new file mode 100644
index 0000000..2c689e8
--- /dev/null
+++ b/php/src/Google/Protobuf/Internal/OneofField.php
@@ -0,0 +1,77 @@
+<?php
+
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+namespace Google\Protobuf\Internal;
+
+class OneofField
+{
+
+    private $desc;
+    private $field_name;
+    private $number = 0;
+    private $value;
+
+    public function __construct($desc)
+    {
+        $this->desc = $desc;
+    }
+
+    public function setValue($value)
+    {
+        $this->value = $value;
+    }
+
+    public function getValue()
+    {
+        return $this->value;
+    }
+
+    public function setFieldName($field_name)
+    {
+        $this->field_name = $field_name;
+    }
+
+    public function getFieldName()
+    {
+        return $this->field_name;
+    }
+
+    public function setNumber($number)
+    {
+        $this->number = $number;
+    }
+
+    public function getNumber()
+    {
+        return $this->number;
+    }
+}
diff --git a/php/src/Google/Protobuf/Internal/OutputStream.php b/php/src/Google/Protobuf/Internal/OutputStream.php
new file mode 100644
index 0000000..fcc5ce6
--- /dev/null
+++ b/php/src/Google/Protobuf/Internal/OutputStream.php
@@ -0,0 +1,143 @@
+<?php
+
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+namespace Google\Protobuf\Internal;
+
+class OutputStream
+{
+
+    private $buffer;
+    private $buffer_size;
+    private $current;
+
+    const MAX_VARINT32_BYTES = 5;
+    const MAX_VARINT64_BYTES = 10;
+
+    public function __construct($size)
+    {
+        $this->current = 0;
+        $this->buffer_size = $size;
+        $this->buffer = str_repeat(chr(0), $this->buffer_size);
+    }
+
+    public function getData()
+    {
+        return $this->buffer;
+    }
+
+    public function writeVarint32($value)
+    {
+        $bytes = str_repeat(chr(0), self::MAX_VARINT32_BYTES);
+        $size = self::writeVarintToArray($value, $bytes, true);
+        return $this->writeRaw($bytes, $size);
+    }
+
+    public function writeVarint64($value)
+    {
+        $bytes = str_repeat(chr(0), self::MAX_VARINT64_BYTES);
+        $size = self::writeVarintToArray($value, $bytes);
+        return $this->writeRaw($bytes, $size);
+    }
+
+    public function writeLittleEndian32($value)
+    {
+        $bytes = str_repeat(chr(0), 4);
+        $size = self::writeLittleEndian32ToArray($value, $bytes);
+        return $this->writeRaw($bytes, $size);
+    }
+
+    public function writeLittleEndian64($value)
+    {
+        $bytes = str_repeat(chr(0), 8);
+        $size = self::writeLittleEndian64ToArray($value, $bytes);
+        return $this->writeRaw($bytes, $size);
+    }
+
+    public function writeTag($tag)
+    {
+        return $this->writeVarint32($tag);
+    }
+
+    public function writeRaw($data, $size)
+    {
+        if ($this->buffer_size < $size) {
+            var_dump($this->buffer_size);
+            var_dump($size);
+            trigger_error("Output stream doesn't have enough buffer.");
+            return false;
+        }
+
+        for ($i = 0; $i < $size; $i++) {
+            $this->buffer[$this->current] = $data[$i];
+            $this->current++;
+            $this->buffer_size--;
+        }
+        return true;
+    }
+
+    private static function writeVarintToArray($value, &$buffer, $trim = false)
+    {
+        $current = 0;
+        if ($trim) {
+            $value &= 0xFFFFFFFF;
+        }
+        while ($value >= 0x80 || $value < 0) {
+            $buffer[$current] = chr($value | 0x80);
+            $value = ($value >> 7) & ~(0x7F << ((PHP_INT_SIZE << 3) - 7));
+            $current++;
+        }
+        $buffer[$current] = chr($value);
+        return $current + 1;
+    }
+
+    private static function writeLittleEndian32ToArray($value, &$buffer)
+    {
+        $buffer[0] = chr($value & 0x000000FF);
+        $buffer[1] = chr(($value >> 8) & 0x000000FF);
+        $buffer[2] = chr(($value >> 16) & 0x000000FF);
+        $buffer[3] = chr(($value >> 24) & 0x000000FF);
+        return 4;
+    }
+
+    private static function writeLittleEndian64ToArray($value, &$buffer)
+    {
+        $buffer[0] = chr($value & 0x000000FF);
+        $buffer[1] = chr(($value >> 8) & 0x000000FF);
+        $buffer[2] = chr(($value >> 16) & 0x000000FF);
+        $buffer[3] = chr(($value >> 24) & 0x000000FF);
+        $buffer[4] = chr(($value >> 32) & 0x000000FF);
+        $buffer[5] = chr(($value >> 40) & 0x000000FF);
+        $buffer[6] = chr(($value >> 48) & 0x000000FF);
+        $buffer[7] = chr(($value >> 56) & 0x000000FF);
+        return 8;
+    }
+}
diff --git a/php/src/Google/Protobuf/Internal/RepeatedField.php b/php/src/Google/Protobuf/Internal/RepeatedField.php
new file mode 100644
index 0000000..0dc5d9d
--- /dev/null
+++ b/php/src/Google/Protobuf/Internal/RepeatedField.php
@@ -0,0 +1,303 @@
+<?php
+
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/**
+ * RepeatedField and RepeatedFieldIter are used by generated protocol message
+ * classes to manipulate repeated fields.
+ */
+
+namespace Google\Protobuf\Internal;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\GPBUtil;
+
+/**
+ * RepeatedFieldIter is used to iterate RepeatedField. It is also need for the
+ * foreach syntax.
+ */
+class RepeatedFieldIter implements \Iterator
+{
+
+    /**
+     * @ignore
+     */
+    private $position;
+    /**
+     * @ignore
+     */
+    private $container;
+
+    /**
+     * Create iterator instance for RepeatedField.
+     *
+     * @param RepeatedField The RepeatedField instance for which this iterator
+     * is created.
+     * @ignore
+     */
+    public function __construct($container)
+    {
+        $this->position = 0;
+        $this->container = $container;
+    }
+
+    /**
+     * Reset the status of the iterator
+     *
+     * @return void
+     */
+    public function rewind()
+    {
+        $this->position = 0;
+    }
+
+    /**
+     * Return the element at the current position.
+     *
+     * @return object The element at the current position.
+     */
+    public function current()
+    {
+        return $this->container[$this->position];
+    }
+
+    /**
+     * Return the current position.
+     *
+     * @return integer The current position.
+     */
+    public function key()
+    {
+        return $this->position;
+    }
+
+    /**
+     * Move to the next position.
+     *
+     * @return void
+     */
+    public function next()
+    {
+        ++$this->position;
+    }
+
+    /**
+     * Check whether there are more elements to iterate.
+     *
+     * @return bool True if there are more elements to iterate.
+     */
+    public function valid()
+    {
+        return isset($this->container[$this->position]);
+    }
+}
+
+/**
+ * RepeatedField is used by generated protocol message classes to manipulate
+ * repeated fields. It can be used like native PHP array.
+ */
+class RepeatedField implements \ArrayAccess, \IteratorAggregate, \Countable
+{
+
+    /**
+     * @ignore
+     */
+    private $container;
+    /**
+     * @ignore
+     */
+    private $type;
+    /**
+     * @ignore
+     */
+    private $klass;
+
+    /**
+     * Constructs an instance of RepeatedField.
+     *
+     * @param long $type Type of the stored element.
+     * @param string $klass Message/Enum class name (message/enum fields only).
+     * @ignore
+     */
+    public function __construct($type, $klass = null)
+    {
+        $this->container = [];
+        $this->type = $type;
+        $this->klass = $klass;
+    }
+
+    /**
+     * @ignore
+     */
+    public function getType()
+    {
+        return $this->type;
+    }
+
+    /**
+     * @ignore
+     */
+    public function getClass()
+    {
+        return $this->klass;
+    }
+
+    /**
+     * Return the element at the given index.
+     *
+     * This will also be called for: $ele = $arr[0]
+     *
+     * @param long $offset The index of the element to be fetched.
+     * @return object The stored element at given index.
+     * @throws ErrorException Invalid type for index.
+     * @throws ErrorException Non-existing index.
+     */
+    public function offsetGet($offset)
+    {
+        return $this->container[$offset];
+    }
+
+    /**
+     * Assign the element at the given index.
+     *
+     * This will also be called for: $arr []= $ele and $arr[0] = ele
+     *
+     * @param long $offset The index of the element to be assigned.
+     * @param object $value The element to be assigned.
+     * @return void
+     * @throws ErrorException Invalid type for index.
+     * @throws ErrorException Non-existing index.
+     * @throws ErrorException Incorrect type of the element.
+     */
+    public function offsetSet($offset, $value)
+    {
+        switch ($this->type) {
+            case GPBType::INT32:
+                GPBUtil::checkInt32($value);
+                break;
+            case GPBType::UINT32:
+                GPBUtil::checkUint32($value);
+                break;
+            case GPBType::INT64:
+                GPBUtil::checkInt64($value);
+                break;
+            case GPBType::UINT64:
+                GPBUtil::checkUint64($value);
+                break;
+            case GPBType::FLOAT:
+                GPBUtil::checkFloat($value);
+                break;
+            case GPBType::DOUBLE:
+                GPBUtil::checkDouble($value);
+                break;
+            case GPBType::BOOL:
+                GPBUtil::checkBool($value);
+                break;
+            case GPBType::STRING:
+                GPBUtil::checkString($value, true);
+                break;
+            case GPBType::MESSAGE:
+                GPBUtil::checkMessage($value, $this->klass);
+                break;
+            default:
+                break;
+        }
+        if (is_null($offset)) {
+            $this->container[] = $value;
+        } else {
+            $count = count($this->container);
+            if (!is_numeric($offset) || $offset < 0 || $offset >= $count) {
+                trigger_error(
+                    "Cannot modify element at the given index",
+                    E_USER_ERROR);
+                return;
+            }
+            $this->container[$offset] = $value;
+        }
+    }
+
+    /**
+     * Remove the element at the given index.
+     *
+     * This will also be called for: unset($arr)
+     *
+     * @param long $offset The index of the element to be removed.
+     * @return void
+     * @throws ErrorException Invalid type for index.
+     * @throws ErrorException The element to be removed is not at the end of the
+     * RepeatedField.
+     */
+    public function offsetUnset($offset)
+    {
+        $count = count($this->container);
+        if (!is_numeric($offset) || $count === 0 || $offset !== $count - 1) {
+            trigger_error(
+                "Cannot remove element at the given index",
+                E_USER_ERROR);
+            return;
+        }
+        array_pop($this->container);
+    }
+
+    /**
+     * Check the existence of the element at the given index.
+     *
+     * This will also be called for: isset($arr)
+     *
+     * @param long $offset The index of the element to be removed.
+     * @return bool True if the element at the given offset exists.
+     * @throws ErrorException Invalid type for index.
+     */
+    public function offsetExists($offset)
+    {
+        return isset($this->container[$offset]);
+    }
+
+    /**
+     * @ignore
+     */
+    public function getIterator()
+    {
+        return new RepeatedFieldIter($this->container);
+    }
+
+    /**
+     * Return the number of stored elements.
+     *
+     * This will also be called for: count($arr)
+     *
+     * @return integer The number of stored elements.
+     */
+    public function count()
+    {
+        return count($this->container);
+    }
+}
diff --git a/php/src/Google/Protobuf/Internal/Type.php b/php/src/Google/Protobuf/Internal/Type.php
new file mode 100644
index 0000000..088f0e0
--- /dev/null
+++ b/php/src/Google/Protobuf/Internal/Type.php
@@ -0,0 +1,175 @@
+<?php
+
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+namespace Google\Protobuf\Internal;
+
+class GPBInteger
+{
+    public $high = 0;
+    public $low = 0;
+
+    public function __construct($value = 0)
+    {
+        $this->low = $value & 0xFFFFFFFF;
+        if (PHP_INT_SIZE === 8) {
+            $this->high = ($value >> 32) & 0xFFFFFFFF;
+        }
+    }
+
+    // Return 0 for unsigned integers and 1 for signed integers.
+    protected function sign()
+    {
+        trigger_error("Not implemented", E_ERROR);
+    }
+
+    public function leftShift($count)
+    {
+        if ($count > 63) {
+            $this->low = 0;
+            $this->high = 0;
+            return;
+        }
+        if ($count > 32) {
+            $this->high = $this->low;
+            $this->low = 0;
+            $count -= 32;
+        }
+        $mask = (1 << $count) - 1;
+        $this->high = (($this->high << $count) & 0xFFFFFFFF) |
+                  (($this->low >> (32 - $count)) & $mask);
+        $this->low = ($this->low << $count) & 0xFFFFFFFF;
+
+        $this->high &= 0xFFFFFFFF;
+        $this->low &= 0xFFFFFFFF;
+        return $this;
+    }
+
+    public function rightShift($count)
+    {
+        $sign = (($this->high & 0x80000000) >> 31) & $this->sign();
+        if ($count > 63) {
+            $this->low = -$sign;
+            $this->high = -$sign;
+            return;
+        }
+        if ($count > 32) {
+            $this->low = $this->high;
+            $this->high = -$sign;
+            $count -= 32;
+        }
+        $this->low = (($this->low >> $count) & 0xFFFFFFFF) |
+                 (($this->high << (32 - $count)) & 0xFFFFFFFF);
+        $this->high = (($this->high >> $count) | (-$sign << $count));
+
+        $this->high &= 0xFFFFFFFF;
+        $this->low &= 0xFFFFFFFF;
+
+        return $this;
+    }
+
+    public function bitOr($var)
+    {
+        $this->high |= $var->high;
+        $this->low |= $var->low;
+        return $this;
+    }
+
+    public function bitXor($var)
+    {
+        $this->high ^= $var->high;
+        $this->low ^= $var->low;
+        return $this;
+    }
+
+    public function bitAnd($var)
+    {
+        $this->high &= $var->high;
+        $this->low &= $var->low;
+        return $this;
+    }
+
+    // Even: all zero; Odd: all one.
+    public function oddMask()
+    {
+        $low = (-($this->low & 1)) & 0xFFFFFFFF;
+        $high = $low;
+        return UInt64::newValue($high, $low);
+    }
+
+    public function toInteger()
+    {
+        if (PHP_INT_SIZE === 8) {
+            return ($this->high << 32) | $this->low;
+        } else {
+            return $this->low;
+        }
+    }
+
+    public function copy()
+    {
+        return static::newValue($this->high, $this->low);
+    }
+}
+
+class Uint64 extends GPBInteger
+{
+
+    public static function newValue($high, $low)
+    {
+        $uint64 = new Uint64(0);
+        $uint64->high = $high;
+        $uint64->low = $low;
+        return $uint64;
+    }
+
+    protected function sign()
+    {
+        return 0;
+    }
+}
+
+class Int64 extends GPBInteger
+{
+
+    public static function newValue($high, $low)
+    {
+        $int64 = new Int64(0);
+        $int64->high = $high;
+        $int64->low = $low;
+        return $int64;
+    }
+
+    protected function sign()
+    {
+        return 1;
+    }
+}
diff --git a/php/src/Google/Protobuf/descriptor.php b/php/src/Google/Protobuf/descriptor.php
new file mode 100644
index 0000000..afe0822
--- /dev/null
+++ b/php/src/Google/Protobuf/descriptor.php
@@ -0,0 +1,541 @@
+<?php
+
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+namespace Google\Protobuf\Internal;
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\MessageOptions;
+
+class FileDescriptor
+{
+
+    private $package;
+    private $message_type = [];
+    private $enum_type = [];
+
+    public function setPackage($package)
+    {
+        $this->package = $package;
+    }
+
+    public function getPackage()
+    {
+        return $this->package;
+    }
+
+    public function getMessageType()
+    {
+        return $this->message_type;
+    }
+
+    public function addMessageType($desc)
+    {
+        $this->message_type[] = $desc;
+    }
+
+    public function getEnumType()
+    {
+        return $this->enum_type;
+    }
+
+    public function addEnumType($desc)
+    {
+        $this->enum_type[]= $desc;
+    }
+
+    public static function buildFromProto($proto)
+    {
+        $file = new FileDescriptor();
+        $file->setPackage($proto->getPackage());
+        foreach ($proto->getMessageType() as $message_proto) {
+            $file->addMessageType(Descriptor::buildFromProto(
+                $message_proto, $file->getPackage(), ""));
+        }
+        foreach ($proto->getEnumType() as $enum_proto) {
+            $file->getEnumType()[] =
+                $file->addEnumType(
+                    EnumDescriptor::buildFromProto(
+                        $enum_proto,
+                        $file->getPackage(),
+                        ""));
+        }
+        return $file;
+    }
+}
+
+class Descriptor
+{
+
+    private $full_name;
+    private $field = [];
+    private $nested_type = [];
+    private $enum_type = [];
+    private $klass;
+    private $options;
+    private $oneof_decl = [];
+
+    public function addOneofDecl($oneof)
+    {
+        $this->oneof_decl[] = $oneof;
+    }
+
+    public function getOneofDecl()
+    {
+        return $this->oneof_decl;
+    }
+
+    public function setFullName($full_name)
+    {
+        $this->full_name = $full_name;
+    }
+
+    public function getFullName()
+    {
+        return $this->full_name;
+    }
+
+    public function addField($field)
+    {
+        $this->field[$field->getNumber()] = $field;
+    }
+
+    public function getField()
+    {
+        return $this->field;
+    }
+
+    public function addNestedType($desc)
+    {
+        $this->nested_type[] = $desc;
+    }
+
+    public function getNestedType()
+    {
+        return $this->nested_type;
+    }
+
+    public function addEnumType($desc)
+    {
+        $this->enum_type[] = $desc;
+    }
+
+    public function getEnumType()
+    {
+        return $this->enum_type;
+    }
+
+    public function getFieldByNumber($number)
+    {
+        return $this->field[$number];
+    }
+
+    public function setClass($klass)
+    {
+        $this->klass = $klass;
+    }
+
+    public function getClass()
+    {
+        return $this->klass;
+    }
+
+    public function setOptions($options)
+    {
+        $this->options = $options;
+    }
+
+    public function getOptions()
+    {
+        return $this->options;
+    }
+
+    public static function buildFromProto($proto, $package, $containing)
+    {
+        $desc = new Descriptor();
+
+        $message_name_without_package  = "";
+        $classname = "";
+        $fullname = "";
+        getFullClassName(
+            $proto,
+            $containing,
+            $package,
+            $message_name_without_package,
+            $classname,
+            $fullname);
+        $desc->setFullName($fullname);
+        $desc->setClass($classname);
+        $desc->setOptions($proto->getOptions());
+
+        foreach ($proto->getField() as $field_proto) {
+            $desc->addField(FieldDescriptor::buildFromProto($field_proto));
+        }
+
+        // Handle nested types.
+        foreach ($proto->getNestedType() as $nested_proto) {
+            $desc->addNestedType(Descriptor::buildFromProto(
+              $nested_proto, $package, $message_name_without_package));
+        }
+
+        // Handle oneof fields.
+        foreach ($proto->getOneofDecl() as $oneof_proto) {
+            $desc->addOneofDecl(
+                OneofDescriptor::buildFromProto($oneof_proto, $desc));
+        }
+
+        return $desc;
+    }
+}
+function getFullClassName(
+    $proto,
+    $containing,
+    $package,
+    &$message_name_without_package,
+    &$classname,
+    &$fullname)
+{
+    // Full name needs to start with '.'.
+    $message_name_without_package = $proto->getName();
+    if ($containing !== "") {
+        $message_name_without_package =
+            $containing . "." . $message_name_without_package;
+    }
+    if ($package === "") {
+        $fullname = "." . $message_name_without_package;
+    } else {
+        $fullname = "." . $package . "." . $message_name_without_package;
+    }
+
+    // Nested message class names are seperated by '_', and package names are
+    // seperated by '\'.
+    $class_name_without_package =
+        implode('_', array_map('ucwords',
+                               explode('.', $message_name_without_package)));
+    $classname =
+        implode('\\', array_map('ucwords', explode('.', $package))).
+        "\\".$class_name_without_package;
+}
+
+class OneofDescriptor
+{
+
+    private $name;
+    private $fields;
+
+    public function setName($name)
+    {
+        $this->name = $name;
+    }
+
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    public function addField(&$field)
+    {
+        $this->fields[] = $field;
+    }
+
+    public function getFields()
+    {
+        return $this->fields;
+    }
+
+    public static function buildFromProto($oneof_proto)
+    {
+        $oneof = new OneofDescriptor();
+        $oneof->setName($oneof_proto->getName());
+        return $oneof;
+    }
+}
+
+
+class EnumDescriptor
+{
+
+    private $klass;
+    private $full_name;
+    private $value;
+
+    public function setFullName($full_name)
+    {
+        $this->full_name = $full_name;
+    }
+
+    public function getFullName()
+    {
+        return $this->full_name;
+    }
+
+    public function addValue($number, $value)
+    {
+        $this->value[$number] = $value;
+    }
+
+    public function setClass($klass)
+    {
+        $this->klass = $klass;
+    }
+
+    public function getClass()
+    {
+        return $this->klass;
+    }
+
+    public static function buildFromProto($proto, $package, $containing)
+    {
+        $desc = new EnumDescriptor();
+
+        $enum_name_without_package  = "";
+        $classname = "";
+        $fullname = "";
+        getFullClassName(
+            $proto,
+            $containing,
+            $package,
+            $enum_name_without_package,
+            $classname,
+            $fullname);
+        $desc->setFullName($fullname);
+        $desc->setClass($classname);
+
+        return $desc;
+    }
+}
+
+class EnumValueDescriptor
+{
+}
+
+class FieldDescriptor
+{
+
+    private $name;
+    private $setter;
+    private $getter;
+    private $number;
+    private $label;
+    private $type;
+    private $message_type;
+    private $enum_type;
+    private $packed;
+    private $is_map;
+    private $oneof_index = -1;
+
+    public function setOneofIndex($index)
+    {
+        $this->oneof_index = $index;
+    }
+
+    public function getOneofIndex()
+    {
+        return $this->oneof_index;
+    }
+
+    public function setName($name)
+    {
+        $this->name = $name;
+    }
+
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    public function setSetter($setter)
+    {
+        $this->setter = $setter;
+    }
+
+    public function getSetter()
+    {
+        return $this->setter;
+    }
+
+    public function setGetter($getter)
+    {
+        $this->getter = $getter;
+    }
+
+    public function getGetter()
+    {
+        return $this->getter;
+    }
+
+    public function setNumber($number)
+    {
+        $this->number = $number;
+    }
+
+    public function getNumber()
+    {
+        return $this->number;
+    }
+
+    public function setLabel($label)
+    {
+        $this->label = $label;
+    }
+
+    public function getLabel()
+    {
+        return $this->label;
+    }
+
+    public function isRepeated()
+    {
+        return $this->label === GPBLabel::REPEATED;
+    }
+
+    public function setType($type)
+    {
+        $this->type = $type;
+    }
+
+    public function getType()
+    {
+        return $this->type;
+    }
+
+    public function setMessageType($message_type)
+    {
+        $this->message_type = $message_type;
+    }
+
+    public function getMessageType()
+    {
+        return $this->message_type;
+    }
+
+    public function setEnumType($enum_type)
+    {
+        $this->enum_type = $enum_type;
+    }
+
+    public function getEnumType()
+    {
+        return $this->enum_type;
+    }
+
+    public function setPacked($packed)
+    {
+        $this->packed = $packed;
+    }
+
+    public function getPacked()
+    {
+        return $this->packed;
+    }
+
+    public function isPackable()
+    {
+        return $this->isRepeated() && self::isTypePackable($this->type);
+    }
+
+    public function isMap()
+    {
+        return $this->getType() == GPBType::MESSAGE &&
+               !is_null($this->getMessageType()->getOptions()) &&
+               $this->getMessageType()->getOptions()->getMapEntry();
+    }
+
+    private static function isTypePackable($field_type)
+    {
+        return ($field_type !== GPBType::STRING  &&
+            $field_type !== GPBType::GROUP   &&
+            $field_type !== GPBType::MESSAGE &&
+            $field_type !== GPBType::BYTES);
+    }
+
+    public static function getFieldDescriptor(
+        $name,
+        $label,
+        $type,
+        $number,
+        $oneof_index,
+        $packed,
+        $type_name = null)
+    {
+        $field = new FieldDescriptor();
+        $field->setName($name);
+        $camel_name = implode('', array_map('ucwords', explode('_', $name)));
+        $field->setGetter('get' . $camel_name);
+        $field->setSetter('set' . $camel_name);
+        $field->setType($type);
+        $field->setNumber($number);
+        $field->setLabel($label);
+        $field->setPacked($packed);
+        $field->setOneofIndex($oneof_index);
+
+        // At this time, the message/enum type may have not been added to pool.
+        // So we use the type name as place holder and will replace it with the
+        // actual descriptor in cross building.
+        switch ($type) {
+            case GPBType::MESSAGE:
+                $field->setMessageType($type_name);
+                break;
+            case GPBType::ENUM:
+                $field->setEnumType($type_name);
+                break;
+            default:
+                break;
+        }
+
+        return $field;
+    }
+
+    public static function buildFromProto($proto)
+    {
+        $type_name = null;
+        switch ($proto->getType()) {
+            case GPBType::MESSAGE:
+            case GPBType::GROUP:
+            case GPBType::ENUM:
+                $type_name = $proto->getTypeName();
+                break;
+            default:
+                break;
+        }
+
+        $oneof_index = $proto->hasOneofIndex() ? $proto->getOneofIndex() : -1;
+        $packed = false;
+        $options = $proto->getOptions();
+        if ($options !== null) {
+            $packed = $options->getPacked();
+        }
+
+        return FieldDescriptor::getFieldDescriptor(
+            $proto->getName(), $proto->getLabel(), $proto->getType(),
+            $proto->getNumber(), $oneof_index, $packed, $type_name);
+    }
+}
diff --git a/php/src/Google/Protobuf/descriptor_internal.pb.php b/php/src/Google/Protobuf/descriptor_internal.pb.php
new file mode 100644
index 0000000..161a9f5
--- /dev/null
+++ b/php/src/Google/Protobuf/descriptor_internal.pb.php
@@ -0,0 +1,2532 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: google/protobuf/descriptor.proto
+
+namespace Google\Protobuf\Internal;
+
+use Google\Protobuf\Internal\DescriptorPool;
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\GPBWire;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\InputStream;
+
+use Google\Protobuf\Internal\GPBUtil;
+
+class FileDescriptorSet extends \Google\Protobuf\Internal\Message
+{
+    private $file;
+    private $has_file = false;
+
+    public function getFile()
+    {
+        return $this->file;
+    }
+
+    public function setFile(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::MESSAGE, \Google\Protobuf\Internal\FileDescriptorProto::class);
+        $this->file = $var;
+        $this->has_file = true;
+    }
+
+    public function hasFile()
+    {
+        return $this->has_file;
+    }
+
+}
+
+class FileDescriptorProto extends \Google\Protobuf\Internal\Message
+{
+    private $name = '';
+    private $has_name = false;
+    private $package = '';
+    private $has_package = false;
+    private $dependency;
+    private $has_dependency = false;
+    private $public_dependency;
+    private $has_public_dependency = false;
+    private $weak_dependency;
+    private $has_weak_dependency = false;
+    private $message_type;
+    private $has_message_type = false;
+    private $enum_type;
+    private $has_enum_type = false;
+    private $service;
+    private $has_service = false;
+    private $extension;
+    private $has_extension = false;
+    private $options = null;
+    private $has_options = false;
+    private $source_code_info = null;
+    private $has_source_code_info = false;
+    private $syntax = '';
+    private $has_syntax = false;
+
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    public function setName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->name = $var;
+        $this->has_name = true;
+    }
+
+    public function hasName()
+    {
+        return $this->has_name;
+    }
+
+    public function getPackage()
+    {
+        return $this->package;
+    }
+
+    public function setPackage($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->package = $var;
+        $this->has_package = true;
+    }
+
+    public function hasPackage()
+    {
+        return $this->has_package;
+    }
+
+    public function getDependency()
+    {
+        return $this->dependency;
+    }
+
+    public function setDependency(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::STRING);
+        $this->dependency = $var;
+        $this->has_dependency = true;
+    }
+
+    public function hasDependency()
+    {
+        return $this->has_dependency;
+    }
+
+    public function getPublicDependency()
+    {
+        return $this->public_dependency;
+    }
+
+    public function setPublicDependency(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::INT32);
+        $this->public_dependency = $var;
+        $this->has_public_dependency = true;
+    }
+
+    public function hasPublicDependency()
+    {
+        return $this->has_public_dependency;
+    }
+
+    public function getWeakDependency()
+    {
+        return $this->weak_dependency;
+    }
+
+    public function setWeakDependency(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::INT32);
+        $this->weak_dependency = $var;
+        $this->has_weak_dependency = true;
+    }
+
+    public function hasWeakDependency()
+    {
+        return $this->has_weak_dependency;
+    }
+
+    public function getMessageType()
+    {
+        return $this->message_type;
+    }
+
+    public function setMessageType(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::MESSAGE, \Google\Protobuf\Internal\DescriptorProto::class);
+        $this->message_type = $var;
+        $this->has_message_type = true;
+    }
+
+    public function hasMessageType()
+    {
+        return $this->has_message_type;
+    }
+
+    public function getEnumType()
+    {
+        return $this->enum_type;
+    }
+
+    public function setEnumType(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::MESSAGE, \Google\Protobuf\Internal\EnumDescriptorProto::class);
+        $this->enum_type = $var;
+        $this->has_enum_type = true;
+    }
+
+    public function hasEnumType()
+    {
+        return $this->has_enum_type;
+    }
+
+    public function getService()
+    {
+        return $this->service;
+    }
+
+    public function setService(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::MESSAGE, \Google\Protobuf\Internal\ServiceDescriptorProto::class);
+        $this->service = $var;
+        $this->has_service = true;
+    }
+
+    public function hasService()
+    {
+        return $this->has_service;
+    }
+
+    public function getExtension()
+    {
+        return $this->extension;
+    }
+
+    public function setExtension(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::MESSAGE, \Google\Protobuf\Internal\FieldDescriptorProto::class);
+        $this->extension = $var;
+        $this->has_extension = true;
+    }
+
+    public function hasExtension()
+    {
+        return $this->has_extension;
+    }
+
+    public function getOptions()
+    {
+        return $this->options;
+    }
+
+    public function setOptions(&$var)
+    {
+        GPBUtil::checkMessage($var, \Google\Protobuf\Internal\FileOptions::class);
+        $this->options = $var;
+        $this->has_options = true;
+    }
+
+    public function hasOptions()
+    {
+        return $this->has_options;
+    }
+
+    public function getSourceCodeInfo()
+    {
+        return $this->source_code_info;
+    }
+
+    public function setSourceCodeInfo(&$var)
+    {
+        GPBUtil::checkMessage($var, \Google\Protobuf\Internal\SourceCodeInfo::class);
+        $this->source_code_info = $var;
+        $this->has_source_code_info = true;
+    }
+
+    public function hasSourceCodeInfo()
+    {
+        return $this->has_source_code_info;
+    }
+
+    public function getSyntax()
+    {
+        return $this->syntax;
+    }
+
+    public function setSyntax($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->syntax = $var;
+        $this->has_syntax = true;
+    }
+
+    public function hasSyntax()
+    {
+        return $this->has_syntax;
+    }
+
+}
+
+class DescriptorProto extends \Google\Protobuf\Internal\Message
+{
+    private $name = '';
+    private $has_name = false;
+    private $field;
+    private $has_field = false;
+    private $extension;
+    private $has_extension = false;
+    private $nested_type;
+    private $has_nested_type = false;
+    private $enum_type;
+    private $has_enum_type = false;
+    private $extension_range;
+    private $has_extension_range = false;
+    private $oneof_decl;
+    private $has_oneof_decl = false;
+    private $options = null;
+    private $has_options = false;
+    private $reserved_range;
+    private $has_reserved_range = false;
+    private $reserved_name;
+    private $has_reserved_name = false;
+
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    public function setName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->name = $var;
+        $this->has_name = true;
+    }
+
+    public function hasName()
+    {
+        return $this->has_name;
+    }
+
+    public function getField()
+    {
+        return $this->field;
+    }
+
+    public function setField(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::MESSAGE, \Google\Protobuf\Internal\FieldDescriptorProto::class);
+        $this->field = $var;
+        $this->has_field = true;
+    }
+
+    public function hasField()
+    {
+        return $this->has_field;
+    }
+
+    public function getExtension()
+    {
+        return $this->extension;
+    }
+
+    public function setExtension(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::MESSAGE, \Google\Protobuf\Internal\FieldDescriptorProto::class);
+        $this->extension = $var;
+        $this->has_extension = true;
+    }
+
+    public function hasExtension()
+    {
+        return $this->has_extension;
+    }
+
+    public function getNestedType()
+    {
+        return $this->nested_type;
+    }
+
+    public function setNestedType(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::MESSAGE, \Google\Protobuf\Internal\DescriptorProto::class);
+        $this->nested_type = $var;
+        $this->has_nested_type = true;
+    }
+
+    public function hasNestedType()
+    {
+        return $this->has_nested_type;
+    }
+
+    public function getEnumType()
+    {
+        return $this->enum_type;
+    }
+
+    public function setEnumType(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::MESSAGE, \Google\Protobuf\Internal\EnumDescriptorProto::class);
+        $this->enum_type = $var;
+        $this->has_enum_type = true;
+    }
+
+    public function hasEnumType()
+    {
+        return $this->has_enum_type;
+    }
+
+    public function getExtensionRange()
+    {
+        return $this->extension_range;
+    }
+
+    public function setExtensionRange(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::MESSAGE, \Google\Protobuf\Internal\DescriptorProto_ExtensionRange::class);
+        $this->extension_range = $var;
+        $this->has_extension_range = true;
+    }
+
+    public function hasExtensionRange()
+    {
+        return $this->has_extension_range;
+    }
+
+    public function getOneofDecl()
+    {
+        return $this->oneof_decl;
+    }
+
+    public function setOneofDecl(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::MESSAGE, \Google\Protobuf\Internal\OneofDescriptorProto::class);
+        $this->oneof_decl = $var;
+        $this->has_oneof_decl = true;
+    }
+
+    public function hasOneofDecl()
+    {
+        return $this->has_oneof_decl;
+    }
+
+    public function getOptions()
+    {
+        return $this->options;
+    }
+
+    public function setOptions(&$var)
+    {
+        GPBUtil::checkMessage($var, \Google\Protobuf\Internal\MessageOptions::class);
+        $this->options = $var;
+        $this->has_options = true;
+    }
+
+    public function hasOptions()
+    {
+        return $this->has_options;
+    }
+
+    public function getReservedRange()
+    {
+        return $this->reserved_range;
+    }
+
+    public function setReservedRange(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::MESSAGE, \Google\Protobuf\Internal\DescriptorProto_ReservedRange::class);
+        $this->reserved_range = $var;
+        $this->has_reserved_range = true;
+    }
+
+    public function hasReservedRange()
+    {
+        return $this->has_reserved_range;
+    }
+
+    public function getReservedName()
+    {
+        return $this->reserved_name;
+    }
+
+    public function setReservedName(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::STRING);
+        $this->reserved_name = $var;
+        $this->has_reserved_name = true;
+    }
+
+    public function hasReservedName()
+    {
+        return $this->has_reserved_name;
+    }
+
+}
+
+class DescriptorProto_ExtensionRange extends \Google\Protobuf\Internal\Message
+{
+    private $start = 0;
+    private $has_start = false;
+    private $end = 0;
+    private $has_end = false;
+
+    public function getStart()
+    {
+        return $this->start;
+    }
+
+    public function setStart($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->start = $var;
+        $this->has_start = true;
+    }
+
+    public function hasStart()
+    {
+        return $this->has_start;
+    }
+
+    public function getEnd()
+    {
+        return $this->end;
+    }
+
+    public function setEnd($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->end = $var;
+        $this->has_end = true;
+    }
+
+    public function hasEnd()
+    {
+        return $this->has_end;
+    }
+
+}
+
+class DescriptorProto_ReservedRange extends \Google\Protobuf\Internal\Message
+{
+    private $start = 0;
+    private $has_start = false;
+    private $end = 0;
+    private $has_end = false;
+
+    public function getStart()
+    {
+        return $this->start;
+    }
+
+    public function setStart($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->start = $var;
+        $this->has_start = true;
+    }
+
+    public function hasStart()
+    {
+        return $this->has_start;
+    }
+
+    public function getEnd()
+    {
+        return $this->end;
+    }
+
+    public function setEnd($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->end = $var;
+        $this->has_end = true;
+    }
+
+    public function hasEnd()
+    {
+        return $this->has_end;
+    }
+
+}
+
+class FieldDescriptorProto extends \Google\Protobuf\Internal\Message
+{
+    private $name = '';
+    private $has_name = false;
+    private $number = 0;
+    private $has_number = false;
+    private $label = 0;
+    private $has_label = false;
+    private $type = 0;
+    private $has_type = false;
+    private $type_name = '';
+    private $has_type_name = false;
+    private $extendee = '';
+    private $has_extendee = false;
+    private $default_value = '';
+    private $has_default_value = false;
+    private $oneof_index = 0;
+    private $has_oneof_index = false;
+    private $json_name = '';
+    private $has_json_name = false;
+    private $options = null;
+    private $has_options = false;
+
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    public function setName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->name = $var;
+        $this->has_name = true;
+    }
+
+    public function hasName()
+    {
+        return $this->has_name;
+    }
+
+    public function getNumber()
+    {
+        return $this->number;
+    }
+
+    public function setNumber($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->number = $var;
+        $this->has_number = true;
+    }
+
+    public function hasNumber()
+    {
+        return $this->has_number;
+    }
+
+    public function getLabel()
+    {
+        return $this->label;
+    }
+
+    public function setLabel($var)
+    {
+        GPBUtil::checkEnum($var, \Google\Protobuf\Internal\FieldDescriptorProto_Label::class);
+        $this->label = $var;
+        $this->has_label = true;
+    }
+
+    public function hasLabel()
+    {
+        return $this->has_label;
+    }
+
+    public function getType()
+    {
+        return $this->type;
+    }
+
+    public function setType($var)
+    {
+        GPBUtil::checkEnum($var, \Google\Protobuf\Internal\FieldDescriptorProto_Type::class);
+        $this->type = $var;
+        $this->has_type = true;
+    }
+
+    public function hasType()
+    {
+        return $this->has_type;
+    }
+
+    public function getTypeName()
+    {
+        return $this->type_name;
+    }
+
+    public function setTypeName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->type_name = $var;
+        $this->has_type_name = true;
+    }
+
+    public function hasTypeName()
+    {
+        return $this->has_type_name;
+    }
+
+    public function getExtendee()
+    {
+        return $this->extendee;
+    }
+
+    public function setExtendee($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->extendee = $var;
+        $this->has_extendee = true;
+    }
+
+    public function hasExtendee()
+    {
+        return $this->has_extendee;
+    }
+
+    public function getDefaultValue()
+    {
+        return $this->default_value;
+    }
+
+    public function setDefaultValue($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->default_value = $var;
+        $this->has_default_value = true;
+    }
+
+    public function hasDefaultValue()
+    {
+        return $this->has_default_value;
+    }
+
+    public function getOneofIndex()
+    {
+        return $this->oneof_index;
+    }
+
+    public function setOneofIndex($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->oneof_index = $var;
+        $this->has_oneof_index = true;
+    }
+
+    public function hasOneofIndex()
+    {
+        return $this->has_oneof_index;
+    }
+
+    public function getJsonName()
+    {
+        return $this->json_name;
+    }
+
+    public function setJsonName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->json_name = $var;
+        $this->has_json_name = true;
+    }
+
+    public function hasJsonName()
+    {
+        return $this->has_json_name;
+    }
+
+    public function getOptions()
+    {
+        return $this->options;
+    }
+
+    public function setOptions(&$var)
+    {
+        GPBUtil::checkMessage($var, \Google\Protobuf\Internal\FieldOptions::class);
+        $this->options = $var;
+        $this->has_options = true;
+    }
+
+    public function hasOptions()
+    {
+        return $this->has_options;
+    }
+
+}
+
+class FieldDescriptorProto_Type
+{
+    const TYPE_DOUBLE = 1;
+    const TYPE_FLOAT = 2;
+    const TYPE_INT64 = 3;
+    const TYPE_UINT64 = 4;
+    const TYPE_INT32 = 5;
+    const TYPE_FIXED64 = 6;
+    const TYPE_FIXED32 = 7;
+    const TYPE_BOOL = 8;
+    const TYPE_STRING = 9;
+    const TYPE_GROUP = 10;
+    const TYPE_MESSAGE = 11;
+    const TYPE_BYTES = 12;
+    const TYPE_UINT32 = 13;
+    const TYPE_ENUM = 14;
+    const TYPE_SFIXED32 = 15;
+    const TYPE_SFIXED64 = 16;
+    const TYPE_SINT32 = 17;
+    const TYPE_SINT64 = 18;
+}
+
+class FieldDescriptorProto_Label
+{
+    const LABEL_OPTIONAL = 1;
+    const LABEL_REQUIRED = 2;
+    const LABEL_REPEATED = 3;
+}
+
+class OneofDescriptorProto extends \Google\Protobuf\Internal\Message
+{
+    private $name = '';
+    private $has_name = false;
+    private $options = null;
+    private $has_options = false;
+
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    public function setName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->name = $var;
+        $this->has_name = true;
+    }
+
+    public function hasName()
+    {
+        return $this->has_name;
+    }
+
+    public function getOptions()
+    {
+        return $this->options;
+    }
+
+    public function setOptions(&$var)
+    {
+        GPBUtil::checkMessage($var, \Google\Protobuf\Internal\OneofOptions::class);
+        $this->options = $var;
+        $this->has_options = true;
+    }
+
+    public function hasOptions()
+    {
+        return $this->has_options;
+    }
+
+}
+
+class EnumDescriptorProto extends \Google\Protobuf\Internal\Message
+{
+    private $name = '';
+    private $has_name = false;
+    private $value;
+    private $has_value = false;
+    private $options = null;
+    private $has_options = false;
+
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    public function setName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->name = $var;
+        $this->has_name = true;
+    }
+
+    public function hasName()
+    {
+        return $this->has_name;
+    }
+
+    public function getValue()
+    {
+        return $this->value;
+    }
+
+    public function setValue(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::MESSAGE, \Google\Protobuf\Internal\EnumValueDescriptorProto::class);
+        $this->value = $var;
+        $this->has_value = true;
+    }
+
+    public function hasValue()
+    {
+        return $this->has_value;
+    }
+
+    public function getOptions()
+    {
+        return $this->options;
+    }
+
+    public function setOptions(&$var)
+    {
+        GPBUtil::checkMessage($var, \Google\Protobuf\Internal\EnumOptions::class);
+        $this->options = $var;
+        $this->has_options = true;
+    }
+
+    public function hasOptions()
+    {
+        return $this->has_options;
+    }
+
+}
+
+class EnumValueDescriptorProto extends \Google\Protobuf\Internal\Message
+{
+    private $name = '';
+    private $has_name = false;
+    private $number = 0;
+    private $has_number = false;
+    private $options = null;
+    private $has_options = false;
+
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    public function setName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->name = $var;
+        $this->has_name = true;
+    }
+
+    public function hasName()
+    {
+        return $this->has_name;
+    }
+
+    public function getNumber()
+    {
+        return $this->number;
+    }
+
+    public function setNumber($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->number = $var;
+        $this->has_number = true;
+    }
+
+    public function hasNumber()
+    {
+        return $this->has_number;
+    }
+
+    public function getOptions()
+    {
+        return $this->options;
+    }
+
+    public function setOptions(&$var)
+    {
+        GPBUtil::checkMessage($var, \Google\Protobuf\Internal\EnumValueOptions::class);
+        $this->options = $var;
+        $this->has_options = true;
+    }
+
+    public function hasOptions()
+    {
+        return $this->has_options;
+    }
+
+}
+
+class ServiceDescriptorProto extends \Google\Protobuf\Internal\Message
+{
+    private $name = '';
+    private $has_name = false;
+    private $method;
+    private $has_method = false;
+    private $options = null;
+    private $has_options = false;
+
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    public function setName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->name = $var;
+        $this->has_name = true;
+    }
+
+    public function hasName()
+    {
+        return $this->has_name;
+    }
+
+    public function getMethod()
+    {
+        return $this->method;
+    }
+
+    public function setMethod(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::MESSAGE, \Google\Protobuf\Internal\MethodDescriptorProto::class);
+        $this->method = $var;
+        $this->has_method = true;
+    }
+
+    public function hasMethod()
+    {
+        return $this->has_method;
+    }
+
+    public function getOptions()
+    {
+        return $this->options;
+    }
+
+    public function setOptions(&$var)
+    {
+        GPBUtil::checkMessage($var, \Google\Protobuf\Internal\ServiceOptions::class);
+        $this->options = $var;
+        $this->has_options = true;
+    }
+
+    public function hasOptions()
+    {
+        return $this->has_options;
+    }
+
+}
+
+class MethodDescriptorProto extends \Google\Protobuf\Internal\Message
+{
+    private $name = '';
+    private $has_name = false;
+    private $input_type = '';
+    private $has_input_type = false;
+    private $output_type = '';
+    private $has_output_type = false;
+    private $options = null;
+    private $has_options = false;
+    private $client_streaming = false;
+    private $has_client_streaming = false;
+    private $server_streaming = false;
+    private $has_server_streaming = false;
+
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    public function setName($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->name = $var;
+        $this->has_name = true;
+    }
+
+    public function hasName()
+    {
+        return $this->has_name;
+    }
+
+    public function getInputType()
+    {
+        return $this->input_type;
+    }
+
+    public function setInputType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->input_type = $var;
+        $this->has_input_type = true;
+    }
+
+    public function hasInputType()
+    {
+        return $this->has_input_type;
+    }
+
+    public function getOutputType()
+    {
+        return $this->output_type;
+    }
+
+    public function setOutputType($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->output_type = $var;
+        $this->has_output_type = true;
+    }
+
+    public function hasOutputType()
+    {
+        return $this->has_output_type;
+    }
+
+    public function getOptions()
+    {
+        return $this->options;
+    }
+
+    public function setOptions(&$var)
+    {
+        GPBUtil::checkMessage($var, \Google\Protobuf\Internal\MethodOptions::class);
+        $this->options = $var;
+        $this->has_options = true;
+    }
+
+    public function hasOptions()
+    {
+        return $this->has_options;
+    }
+
+    public function getClientStreaming()
+    {
+        return $this->client_streaming;
+    }
+
+    public function setClientStreaming($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->client_streaming = $var;
+        $this->has_client_streaming = true;
+    }
+
+    public function hasClientStreaming()
+    {
+        return $this->has_client_streaming;
+    }
+
+    public function getServerStreaming()
+    {
+        return $this->server_streaming;
+    }
+
+    public function setServerStreaming($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->server_streaming = $var;
+        $this->has_server_streaming = true;
+    }
+
+    public function hasServerStreaming()
+    {
+        return $this->has_server_streaming;
+    }
+
+}
+
+class FileOptions extends \Google\Protobuf\Internal\Message
+{
+    private $java_package = '';
+    private $has_java_package = false;
+    private $java_outer_classname = '';
+    private $has_java_outer_classname = false;
+    private $java_multiple_files = false;
+    private $has_java_multiple_files = false;
+    private $java_generate_equals_and_hash = false;
+    private $has_java_generate_equals_and_hash = false;
+    private $java_string_check_utf8 = false;
+    private $has_java_string_check_utf8 = false;
+    private $optimize_for = 0;
+    private $has_optimize_for = false;
+    private $go_package = '';
+    private $has_go_package = false;
+    private $cc_generic_services = false;
+    private $has_cc_generic_services = false;
+    private $java_generic_services = false;
+    private $has_java_generic_services = false;
+    private $py_generic_services = false;
+    private $has_py_generic_services = false;
+    private $deprecated = false;
+    private $has_deprecated = false;
+    private $cc_enable_arenas = false;
+    private $has_cc_enable_arenas = false;
+    private $objc_class_prefix = '';
+    private $has_objc_class_prefix = false;
+    private $csharp_namespace = '';
+    private $has_csharp_namespace = false;
+    private $uninterpreted_option;
+    private $has_uninterpreted_option = false;
+
+    public function getJavaPackage()
+    {
+        return $this->java_package;
+    }
+
+    public function setJavaPackage($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->java_package = $var;
+        $this->has_java_package = true;
+    }
+
+    public function hasJavaPackage()
+    {
+        return $this->has_java_package;
+    }
+
+    public function getJavaOuterClassname()
+    {
+        return $this->java_outer_classname;
+    }
+
+    public function setJavaOuterClassname($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->java_outer_classname = $var;
+        $this->has_java_outer_classname = true;
+    }
+
+    public function hasJavaOuterClassname()
+    {
+        return $this->has_java_outer_classname;
+    }
+
+    public function getJavaMultipleFiles()
+    {
+        return $this->java_multiple_files;
+    }
+
+    public function setJavaMultipleFiles($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->java_multiple_files = $var;
+        $this->has_java_multiple_files = true;
+    }
+
+    public function hasJavaMultipleFiles()
+    {
+        return $this->has_java_multiple_files;
+    }
+
+    public function getJavaGenerateEqualsAndHash()
+    {
+        return $this->java_generate_equals_and_hash;
+    }
+
+    public function setJavaGenerateEqualsAndHash($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->java_generate_equals_and_hash = $var;
+        $this->has_java_generate_equals_and_hash = true;
+    }
+
+    public function hasJavaGenerateEqualsAndHash()
+    {
+        return $this->has_java_generate_equals_and_hash;
+    }
+
+    public function getJavaStringCheckUtf8()
+    {
+        return $this->java_string_check_utf8;
+    }
+
+    public function setJavaStringCheckUtf8($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->java_string_check_utf8 = $var;
+        $this->has_java_string_check_utf8 = true;
+    }
+
+    public function hasJavaStringCheckUtf8()
+    {
+        return $this->has_java_string_check_utf8;
+    }
+
+    public function getOptimizeFor()
+    {
+        return $this->optimize_for;
+    }
+
+    public function setOptimizeFor($var)
+    {
+        GPBUtil::checkEnum($var, \Google\Protobuf\Internal\FileOptions_OptimizeMode::class);
+        $this->optimize_for = $var;
+        $this->has_optimize_for = true;
+    }
+
+    public function hasOptimizeFor()
+    {
+        return $this->has_optimize_for;
+    }
+
+    public function getGoPackage()
+    {
+        return $this->go_package;
+    }
+
+    public function setGoPackage($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->go_package = $var;
+        $this->has_go_package = true;
+    }
+
+    public function hasGoPackage()
+    {
+        return $this->has_go_package;
+    }
+
+    public function getCcGenericServices()
+    {
+        return $this->cc_generic_services;
+    }
+
+    public function setCcGenericServices($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->cc_generic_services = $var;
+        $this->has_cc_generic_services = true;
+    }
+
+    public function hasCcGenericServices()
+    {
+        return $this->has_cc_generic_services;
+    }
+
+    public function getJavaGenericServices()
+    {
+        return $this->java_generic_services;
+    }
+
+    public function setJavaGenericServices($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->java_generic_services = $var;
+        $this->has_java_generic_services = true;
+    }
+
+    public function hasJavaGenericServices()
+    {
+        return $this->has_java_generic_services;
+    }
+
+    public function getPyGenericServices()
+    {
+        return $this->py_generic_services;
+    }
+
+    public function setPyGenericServices($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->py_generic_services = $var;
+        $this->has_py_generic_services = true;
+    }
+
+    public function hasPyGenericServices()
+    {
+        return $this->has_py_generic_services;
+    }
+
+    public function getDeprecated()
+    {
+        return $this->deprecated;
+    }
+
+    public function setDeprecated($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->deprecated = $var;
+        $this->has_deprecated = true;
+    }
+
+    public function hasDeprecated()
+    {
+        return $this->has_deprecated;
+    }
+
+    public function getCcEnableArenas()
+    {
+        return $this->cc_enable_arenas;
+    }
+
+    public function setCcEnableArenas($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->cc_enable_arenas = $var;
+        $this->has_cc_enable_arenas = true;
+    }
+
+    public function hasCcEnableArenas()
+    {
+        return $this->has_cc_enable_arenas;
+    }
+
+    public function getObjcClassPrefix()
+    {
+        return $this->objc_class_prefix;
+    }
+
+    public function setObjcClassPrefix($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->objc_class_prefix = $var;
+        $this->has_objc_class_prefix = true;
+    }
+
+    public function hasObjcClassPrefix()
+    {
+        return $this->has_objc_class_prefix;
+    }
+
+    public function getCsharpNamespace()
+    {
+        return $this->csharp_namespace;
+    }
+
+    public function setCsharpNamespace($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->csharp_namespace = $var;
+        $this->has_csharp_namespace = true;
+    }
+
+    public function hasCsharpNamespace()
+    {
+        return $this->has_csharp_namespace;
+    }
+
+    public function getUninterpretedOption()
+    {
+        return $this->uninterpreted_option;
+    }
+
+    public function setUninterpretedOption(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::MESSAGE, \Google\Protobuf\Internal\UninterpretedOption::class);
+        $this->uninterpreted_option = $var;
+        $this->has_uninterpreted_option = true;
+    }
+
+    public function hasUninterpretedOption()
+    {
+        return $this->has_uninterpreted_option;
+    }
+
+}
+
+class FileOptions_OptimizeMode
+{
+    const SPEED = 1;
+    const CODE_SIZE = 2;
+    const LITE_RUNTIME = 3;
+}
+
+class MessageOptions extends \Google\Protobuf\Internal\Message
+{
+    private $message_set_wire_format = false;
+    private $has_message_set_wire_format = false;
+    private $no_standard_descriptor_accessor = false;
+    private $has_no_standard_descriptor_accessor = false;
+    private $deprecated = false;
+    private $has_deprecated = false;
+    private $map_entry = false;
+    private $has_map_entry = false;
+    private $uninterpreted_option;
+    private $has_uninterpreted_option = false;
+
+    public function getMessageSetWireFormat()
+    {
+        return $this->message_set_wire_format;
+    }
+
+    public function setMessageSetWireFormat($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->message_set_wire_format = $var;
+        $this->has_message_set_wire_format = true;
+    }
+
+    public function hasMessageSetWireFormat()
+    {
+        return $this->has_message_set_wire_format;
+    }
+
+    public function getNoStandardDescriptorAccessor()
+    {
+        return $this->no_standard_descriptor_accessor;
+    }
+
+    public function setNoStandardDescriptorAccessor($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->no_standard_descriptor_accessor = $var;
+        $this->has_no_standard_descriptor_accessor = true;
+    }
+
+    public function hasNoStandardDescriptorAccessor()
+    {
+        return $this->has_no_standard_descriptor_accessor;
+    }
+
+    public function getDeprecated()
+    {
+        return $this->deprecated;
+    }
+
+    public function setDeprecated($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->deprecated = $var;
+        $this->has_deprecated = true;
+    }
+
+    public function hasDeprecated()
+    {
+        return $this->has_deprecated;
+    }
+
+    public function getMapEntry()
+    {
+        return $this->map_entry;
+    }
+
+    public function setMapEntry($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->map_entry = $var;
+        $this->has_map_entry = true;
+    }
+
+    public function hasMapEntry()
+    {
+        return $this->has_map_entry;
+    }
+
+    public function getUninterpretedOption()
+    {
+        return $this->uninterpreted_option;
+    }
+
+    public function setUninterpretedOption(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::MESSAGE, \Google\Protobuf\Internal\UninterpretedOption::class);
+        $this->uninterpreted_option = $var;
+        $this->has_uninterpreted_option = true;
+    }
+
+    public function hasUninterpretedOption()
+    {
+        return $this->has_uninterpreted_option;
+    }
+
+}
+
+class FieldOptions extends \Google\Protobuf\Internal\Message
+{
+    private $ctype = 0;
+    private $has_ctype = false;
+    private $packed = false;
+    private $has_packed = false;
+    private $jstype = 0;
+    private $has_jstype = false;
+    private $lazy = false;
+    private $has_lazy = false;
+    private $deprecated = false;
+    private $has_deprecated = false;
+    private $weak = false;
+    private $has_weak = false;
+    private $uninterpreted_option;
+    private $has_uninterpreted_option = false;
+
+    public function getCtype()
+    {
+        return $this->ctype;
+    }
+
+    public function setCtype($var)
+    {
+        GPBUtil::checkEnum($var, \Google\Protobuf\Internal\FieldOptions_CType::class);
+        $this->ctype = $var;
+        $this->has_ctype = true;
+    }
+
+    public function hasCtype()
+    {
+        return $this->has_ctype;
+    }
+
+    public function getPacked()
+    {
+        return $this->packed;
+    }
+
+    public function setPacked($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->packed = $var;
+        $this->has_packed = true;
+    }
+
+    public function hasPacked()
+    {
+        return $this->has_packed;
+    }
+
+    public function getJstype()
+    {
+        return $this->jstype;
+    }
+
+    public function setJstype($var)
+    {
+        GPBUtil::checkEnum($var, \Google\Protobuf\Internal\FieldOptions_JSType::class);
+        $this->jstype = $var;
+        $this->has_jstype = true;
+    }
+
+    public function hasJstype()
+    {
+        return $this->has_jstype;
+    }
+
+    public function getLazy()
+    {
+        return $this->lazy;
+    }
+
+    public function setLazy($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->lazy = $var;
+        $this->has_lazy = true;
+    }
+
+    public function hasLazy()
+    {
+        return $this->has_lazy;
+    }
+
+    public function getDeprecated()
+    {
+        return $this->deprecated;
+    }
+
+    public function setDeprecated($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->deprecated = $var;
+        $this->has_deprecated = true;
+    }
+
+    public function hasDeprecated()
+    {
+        return $this->has_deprecated;
+    }
+
+    public function getWeak()
+    {
+        return $this->weak;
+    }
+
+    public function setWeak($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->weak = $var;
+        $this->has_weak = true;
+    }
+
+    public function hasWeak()
+    {
+        return $this->has_weak;
+    }
+
+    public function getUninterpretedOption()
+    {
+        return $this->uninterpreted_option;
+    }
+
+    public function setUninterpretedOption(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::MESSAGE, \Google\Protobuf\Internal\UninterpretedOption::class);
+        $this->uninterpreted_option = $var;
+        $this->has_uninterpreted_option = true;
+    }
+
+    public function hasUninterpretedOption()
+    {
+        return $this->has_uninterpreted_option;
+    }
+
+}
+
+class FieldOptions_CType
+{
+    const STRING = 0;
+    const CORD = 1;
+    const STRING_PIECE = 2;
+}
+
+class FieldOptions_JSType
+{
+    const JS_NORMAL = 0;
+    const JS_STRING = 1;
+    const JS_NUMBER = 2;
+}
+
+class OneofOptions extends \Google\Protobuf\Internal\Message
+{
+    private $uninterpreted_option;
+    private $has_uninterpreted_option = false;
+
+    public function getUninterpretedOption()
+    {
+        return $this->uninterpreted_option;
+    }
+
+    public function setUninterpretedOption(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::MESSAGE, \Google\Protobuf\Internal\UninterpretedOption::class);
+        $this->uninterpreted_option = $var;
+        $this->has_uninterpreted_option = true;
+    }
+
+    public function hasUninterpretedOption()
+    {
+        return $this->has_uninterpreted_option;
+    }
+
+}
+
+class EnumOptions extends \Google\Protobuf\Internal\Message
+{
+    private $allow_alias = false;
+    private $has_allow_alias = false;
+    private $deprecated = false;
+    private $has_deprecated = false;
+    private $uninterpreted_option;
+    private $has_uninterpreted_option = false;
+
+    public function getAllowAlias()
+    {
+        return $this->allow_alias;
+    }
+
+    public function setAllowAlias($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->allow_alias = $var;
+        $this->has_allow_alias = true;
+    }
+
+    public function hasAllowAlias()
+    {
+        return $this->has_allow_alias;
+    }
+
+    public function getDeprecated()
+    {
+        return $this->deprecated;
+    }
+
+    public function setDeprecated($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->deprecated = $var;
+        $this->has_deprecated = true;
+    }
+
+    public function hasDeprecated()
+    {
+        return $this->has_deprecated;
+    }
+
+    public function getUninterpretedOption()
+    {
+        return $this->uninterpreted_option;
+    }
+
+    public function setUninterpretedOption(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::MESSAGE, \Google\Protobuf\Internal\UninterpretedOption::class);
+        $this->uninterpreted_option = $var;
+        $this->has_uninterpreted_option = true;
+    }
+
+    public function hasUninterpretedOption()
+    {
+        return $this->has_uninterpreted_option;
+    }
+
+}
+
+class EnumValueOptions extends \Google\Protobuf\Internal\Message
+{
+    private $deprecated = false;
+    private $has_deprecated = false;
+    private $uninterpreted_option;
+    private $has_uninterpreted_option = false;
+
+    public function getDeprecated()
+    {
+        return $this->deprecated;
+    }
+
+    public function setDeprecated($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->deprecated = $var;
+        $this->has_deprecated = true;
+    }
+
+    public function hasDeprecated()
+    {
+        return $this->has_deprecated;
+    }
+
+    public function getUninterpretedOption()
+    {
+        return $this->uninterpreted_option;
+    }
+
+    public function setUninterpretedOption(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::MESSAGE, \Google\Protobuf\Internal\UninterpretedOption::class);
+        $this->uninterpreted_option = $var;
+        $this->has_uninterpreted_option = true;
+    }
+
+    public function hasUninterpretedOption()
+    {
+        return $this->has_uninterpreted_option;
+    }
+
+}
+
+class ServiceOptions extends \Google\Protobuf\Internal\Message
+{
+    private $deprecated = false;
+    private $has_deprecated = false;
+    private $uninterpreted_option;
+    private $has_uninterpreted_option = false;
+
+    public function getDeprecated()
+    {
+        return $this->deprecated;
+    }
+
+    public function setDeprecated($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->deprecated = $var;
+        $this->has_deprecated = true;
+    }
+
+    public function hasDeprecated()
+    {
+        return $this->has_deprecated;
+    }
+
+    public function getUninterpretedOption()
+    {
+        return $this->uninterpreted_option;
+    }
+
+    public function setUninterpretedOption(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::MESSAGE, \Google\Protobuf\Internal\UninterpretedOption::class);
+        $this->uninterpreted_option = $var;
+        $this->has_uninterpreted_option = true;
+    }
+
+    public function hasUninterpretedOption()
+    {
+        return $this->has_uninterpreted_option;
+    }
+
+}
+
+class MethodOptions extends \Google\Protobuf\Internal\Message
+{
+    private $deprecated = false;
+    private $has_deprecated = false;
+    private $uninterpreted_option;
+    private $has_uninterpreted_option = false;
+
+    public function getDeprecated()
+    {
+        return $this->deprecated;
+    }
+
+    public function setDeprecated($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->deprecated = $var;
+        $this->has_deprecated = true;
+    }
+
+    public function hasDeprecated()
+    {
+        return $this->has_deprecated;
+    }
+
+    public function getUninterpretedOption()
+    {
+        return $this->uninterpreted_option;
+    }
+
+    public function setUninterpretedOption(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::MESSAGE, \Google\Protobuf\Internal\UninterpretedOption::class);
+        $this->uninterpreted_option = $var;
+        $this->has_uninterpreted_option = true;
+    }
+
+    public function hasUninterpretedOption()
+    {
+        return $this->has_uninterpreted_option;
+    }
+
+}
+
+class UninterpretedOption extends \Google\Protobuf\Internal\Message
+{
+    private $name;
+    private $has_name = false;
+    private $identifier_value = '';
+    private $has_identifier_value = false;
+    private $positive_int_value = 0;
+    private $has_positive_int_value = false;
+    private $negative_int_value = 0;
+    private $has_negative_int_value = false;
+    private $double_value = 0.0;
+    private $has_double_value = false;
+    private $string_value = '';
+    private $has_string_value = false;
+    private $aggregate_value = '';
+    private $has_aggregate_value = false;
+
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    public function setName(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::MESSAGE, \Google\Protobuf\Internal\UninterpretedOption_NamePart::class);
+        $this->name = $var;
+        $this->has_name = true;
+    }
+
+    public function hasName()
+    {
+        return $this->has_name;
+    }
+
+    public function getIdentifierValue()
+    {
+        return $this->identifier_value;
+    }
+
+    public function setIdentifierValue($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->identifier_value = $var;
+        $this->has_identifier_value = true;
+    }
+
+    public function hasIdentifierValue()
+    {
+        return $this->has_identifier_value;
+    }
+
+    public function getPositiveIntValue()
+    {
+        return $this->positive_int_value;
+    }
+
+    public function setPositiveIntValue($var)
+    {
+        GPBUtil::checkUint64($var);
+        $this->positive_int_value = $var;
+        $this->has_positive_int_value = true;
+    }
+
+    public function hasPositiveIntValue()
+    {
+        return $this->has_positive_int_value;
+    }
+
+    public function getNegativeIntValue()
+    {
+        return $this->negative_int_value;
+    }
+
+    public function setNegativeIntValue($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->negative_int_value = $var;
+        $this->has_negative_int_value = true;
+    }
+
+    public function hasNegativeIntValue()
+    {
+        return $this->has_negative_int_value;
+    }
+
+    public function getDoubleValue()
+    {
+        return $this->double_value;
+    }
+
+    public function setDoubleValue($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->double_value = $var;
+        $this->has_double_value = true;
+    }
+
+    public function hasDoubleValue()
+    {
+        return $this->has_double_value;
+    }
+
+    public function getStringValue()
+    {
+        return $this->string_value;
+    }
+
+    public function setStringValue($var)
+    {
+        GPBUtil::checkString($var, False);
+        $this->string_value = $var;
+        $this->has_string_value = true;
+    }
+
+    public function hasStringValue()
+    {
+        return $this->has_string_value;
+    }
+
+    public function getAggregateValue()
+    {
+        return $this->aggregate_value;
+    }
+
+    public function setAggregateValue($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->aggregate_value = $var;
+        $this->has_aggregate_value = true;
+    }
+
+    public function hasAggregateValue()
+    {
+        return $this->has_aggregate_value;
+    }
+
+}
+
+class UninterpretedOption_NamePart extends \Google\Protobuf\Internal\Message
+{
+    private $name_part = '';
+    private $has_name_part = false;
+    private $is_extension = false;
+    private $has_is_extension = false;
+
+    public function getNamePart()
+    {
+        return $this->name_part;
+    }
+
+    public function setNamePart($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->name_part = $var;
+        $this->has_name_part = true;
+    }
+
+    public function hasNamePart()
+    {
+        return $this->has_name_part;
+    }
+
+    public function getIsExtension()
+    {
+        return $this->is_extension;
+    }
+
+    public function setIsExtension($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->is_extension = $var;
+        $this->has_is_extension = true;
+    }
+
+    public function hasIsExtension()
+    {
+        return $this->has_is_extension;
+    }
+
+}
+
+class SourceCodeInfo extends \Google\Protobuf\Internal\Message
+{
+    private $location;
+    private $has_location = false;
+
+    public function getLocation()
+    {
+        return $this->location;
+    }
+
+    public function setLocation(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::MESSAGE, \Google\Protobuf\Internal\SourceCodeInfo_Location::class);
+        $this->location = $var;
+        $this->has_location = true;
+    }
+
+    public function hasLocation()
+    {
+        return $this->has_location;
+    }
+
+}
+
+class SourceCodeInfo_Location extends \Google\Protobuf\Internal\Message
+{
+    private $path;
+    private $has_path = false;
+    private $span;
+    private $has_span = false;
+    private $leading_comments = '';
+    private $has_leading_comments = false;
+    private $trailing_comments = '';
+    private $has_trailing_comments = false;
+    private $leading_detached_comments;
+    private $has_leading_detached_comments = false;
+
+    public function getPath()
+    {
+        return $this->path;
+    }
+
+    public function setPath(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::INT32);
+        $this->path = $var;
+        $this->has_path = true;
+    }
+
+    public function hasPath()
+    {
+        return $this->has_path;
+    }
+
+    public function getSpan()
+    {
+        return $this->span;
+    }
+
+    public function setSpan(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::INT32);
+        $this->span = $var;
+        $this->has_span = true;
+    }
+
+    public function hasSpan()
+    {
+        return $this->has_span;
+    }
+
+    public function getLeadingComments()
+    {
+        return $this->leading_comments;
+    }
+
+    public function setLeadingComments($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->leading_comments = $var;
+        $this->has_leading_comments = true;
+    }
+
+    public function hasLeadingComments()
+    {
+        return $this->has_leading_comments;
+    }
+
+    public function getTrailingComments()
+    {
+        return $this->trailing_comments;
+    }
+
+    public function setTrailingComments($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->trailing_comments = $var;
+        $this->has_trailing_comments = true;
+    }
+
+    public function hasTrailingComments()
+    {
+        return $this->has_trailing_comments;
+    }
+
+    public function getLeadingDetachedComments()
+    {
+        return $this->leading_detached_comments;
+    }
+
+    public function setLeadingDetachedComments(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::STRING);
+        $this->leading_detached_comments = $var;
+        $this->has_leading_detached_comments = true;
+    }
+
+    public function hasLeadingDetachedComments()
+    {
+        return $this->has_leading_detached_comments;
+    }
+
+}
+
+class GeneratedCodeInfo extends \Google\Protobuf\Internal\Message
+{
+    private $annotation;
+    private $has_annotation = false;
+
+    public function getAnnotation()
+    {
+        return $this->annotation;
+    }
+
+    public function setAnnotation(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::MESSAGE, \Google\Protobuf\Internal\GeneratedCodeInfo_Annotation::class);
+        $this->annotation = $var;
+        $this->has_annotation = true;
+    }
+
+    public function hasAnnotation()
+    {
+        return $this->has_annotation;
+    }
+
+}
+
+class GeneratedCodeInfo_Annotation extends \Google\Protobuf\Internal\Message
+{
+    private $path;
+    private $has_path = false;
+    private $source_file = '';
+    private $has_source_file = false;
+    private $begin = 0;
+    private $has_begin = false;
+    private $end = 0;
+    private $has_end = false;
+
+    public function getPath()
+    {
+        return $this->path;
+    }
+
+    public function setPath(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::INT32);
+        $this->path = $var;
+        $this->has_path = true;
+    }
+
+    public function hasPath()
+    {
+        return $this->has_path;
+    }
+
+    public function getSourceFile()
+    {
+        return $this->source_file;
+    }
+
+    public function setSourceFile($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->source_file = $var;
+        $this->has_source_file = true;
+    }
+
+    public function hasSourceFile()
+    {
+        return $this->has_source_file;
+    }
+
+    public function getBegin()
+    {
+        return $this->begin;
+    }
+
+    public function setBegin($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->begin = $var;
+        $this->has_begin = true;
+    }
+
+    public function hasBegin()
+    {
+        return $this->has_begin;
+    }
+
+    public function getEnd()
+    {
+        return $this->end;
+    }
+
+    public function setEnd($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->end = $var;
+        $this->has_end = true;
+    }
+
+    public function hasEnd()
+    {
+        return $this->has_end;
+    }
+
+}
+
+$pool = DescriptorPool::getGeneratedPool();
+
+$pool->addMessage('google.protobuf.internal.FileDescriptorSet', FileDescriptorSet::class)
+    ->repeated('file', GPBType::MESSAGE, 1, 'google.protobuf.internal.FileDescriptorProto')
+    ->finalizeToPool();
+
+$pool->addMessage('google.protobuf.internal.FileDescriptorProto', FileDescriptorProto::class)
+    ->optional('name', GPBType::STRING, 1)
+    ->optional('package', GPBType::STRING, 2)
+    ->repeated('dependency', GPBType::STRING, 3)
+    ->repeated('public_dependency', GPBType::INT32, 10)
+    ->repeated('weak_dependency', GPBType::INT32, 11)
+    ->repeated('message_type', GPBType::MESSAGE, 4, 'google.protobuf.internal.DescriptorProto')
+    ->repeated('enum_type', GPBType::MESSAGE, 5, 'google.protobuf.internal.EnumDescriptorProto')
+    ->repeated('service', GPBType::MESSAGE, 6, 'google.protobuf.internal.ServiceDescriptorProto')
+    ->repeated('extension', GPBType::MESSAGE, 7, 'google.protobuf.internal.FieldDescriptorProto')
+    ->optional('options', GPBType::MESSAGE, 8, 'google.protobuf.internal.FileOptions')
+    ->optional('source_code_info', GPBType::MESSAGE, 9, 'google.protobuf.internal.SourceCodeInfo')
+    ->optional('syntax', GPBType::STRING, 12)
+    ->finalizeToPool();
+
+$pool->addMessage('google.protobuf.internal.DescriptorProto', DescriptorProto::class)
+    ->optional('name', GPBType::STRING, 1)
+    ->repeated('field', GPBType::MESSAGE, 2, 'google.protobuf.internal.FieldDescriptorProto')
+    ->repeated('extension', GPBType::MESSAGE, 6, 'google.protobuf.internal.FieldDescriptorProto')
+    ->repeated('nested_type', GPBType::MESSAGE, 3, 'google.protobuf.internal.DescriptorProto')
+    ->repeated('enum_type', GPBType::MESSAGE, 4, 'google.protobuf.internal.EnumDescriptorProto')
+    ->repeated('extension_range', GPBType::MESSAGE, 5, 'google.protobuf.internal.DescriptorProto.ExtensionRange')
+    ->repeated('oneof_decl', GPBType::MESSAGE, 8, 'google.protobuf.internal.OneofDescriptorProto')
+    ->optional('options', GPBType::MESSAGE, 7, 'google.protobuf.internal.MessageOptions')
+    ->repeated('reserved_range', GPBType::MESSAGE, 9, 'google.protobuf.internal.DescriptorProto.ReservedRange')
+    ->repeated('reserved_name', GPBType::STRING, 10)
+    ->finalizeToPool();
+
+$pool->addMessage('google.protobuf.internal.DescriptorProto.ExtensionRange', DescriptorProto_ExtensionRange::class)
+    ->optional('start', GPBType::INT32, 1)
+    ->optional('end', GPBType::INT32, 2)
+    ->finalizeToPool();
+
+$pool->addMessage('google.protobuf.internal.DescriptorProto.ReservedRange', DescriptorProto_ReservedRange::class)
+    ->optional('start', GPBType::INT32, 1)
+    ->optional('end', GPBType::INT32, 2)
+    ->finalizeToPool();
+
+$pool->addMessage('google.protobuf.internal.FieldDescriptorProto', FieldDescriptorProto::class)
+    ->optional('name', GPBType::STRING, 1)
+    ->optional('number', GPBType::INT32, 3)
+    ->optional('label', GPBType::ENUM, 4, 'google.protobuf.internal.FieldDescriptorProto.Label')
+    ->optional('type', GPBType::ENUM, 5, 'google.protobuf.internal.FieldDescriptorProto.Type')
+    ->optional('type_name', GPBType::STRING, 6)
+    ->optional('extendee', GPBType::STRING, 2)
+    ->optional('default_value', GPBType::STRING, 7)
+    ->optional('oneof_index', GPBType::INT32, 9)
+    ->optional('json_name', GPBType::STRING, 10)
+    ->optional('options', GPBType::MESSAGE, 8, 'google.protobuf.internal.FieldOptions')
+    ->finalizeToPool();
+
+$pool->addEnum('google.protobuf.internal.FieldDescriptorProto.Type', Type::class)
+    ->value("TYPE_DOUBLE", 1)
+    ->value("TYPE_FLOAT", 2)
+    ->value("TYPE_INT64", 3)
+    ->value("TYPE_UINT64", 4)
+    ->value("TYPE_INT32", 5)
+    ->value("TYPE_FIXED64", 6)
+    ->value("TYPE_FIXED32", 7)
+    ->value("TYPE_BOOL", 8)
+    ->value("TYPE_STRING", 9)
+    ->value("TYPE_GROUP", 10)
+    ->value("TYPE_MESSAGE", 11)
+    ->value("TYPE_BYTES", 12)
+    ->value("TYPE_UINT32", 13)
+    ->value("TYPE_ENUM", 14)
+    ->value("TYPE_SFIXED32", 15)
+    ->value("TYPE_SFIXED64", 16)
+    ->value("TYPE_SINT32", 17)
+    ->value("TYPE_SINT64", 18)
+    ->finalizeToPool();
+
+$pool->addEnum('google.protobuf.internal.FieldDescriptorProto.Label', Label::class)
+    ->value("LABEL_OPTIONAL", 1)
+    ->value("LABEL_REQUIRED", 2)
+    ->value("LABEL_REPEATED", 3)
+    ->finalizeToPool();
+
+$pool->addMessage('google.protobuf.internal.OneofDescriptorProto', OneofDescriptorProto::class)
+    ->optional('name', GPBType::STRING, 1)
+    ->optional('options', GPBType::MESSAGE, 2, 'google.protobuf.internal.OneofOptions')
+    ->finalizeToPool();
+
+$pool->addMessage('google.protobuf.internal.EnumDescriptorProto', EnumDescriptorProto::class)
+    ->optional('name', GPBType::STRING, 1)
+    ->repeated('value', GPBType::MESSAGE, 2, 'google.protobuf.internal.EnumValueDescriptorProto')
+    ->optional('options', GPBType::MESSAGE, 3, 'google.protobuf.internal.EnumOptions')
+    ->finalizeToPool();
+
+$pool->addMessage('google.protobuf.internal.EnumValueDescriptorProto', EnumValueDescriptorProto::class)
+    ->optional('name', GPBType::STRING, 1)
+    ->optional('number', GPBType::INT32, 2)
+    ->optional('options', GPBType::MESSAGE, 3, 'google.protobuf.internal.EnumValueOptions')
+    ->finalizeToPool();
+
+$pool->addMessage('google.protobuf.internal.ServiceDescriptorProto', ServiceDescriptorProto::class)
+    ->optional('name', GPBType::STRING, 1)
+    ->repeated('method', GPBType::MESSAGE, 2, 'google.protobuf.internal.MethodDescriptorProto')
+    ->optional('options', GPBType::MESSAGE, 3, 'google.protobuf.internal.ServiceOptions')
+    ->finalizeToPool();
+
+$pool->addMessage('google.protobuf.internal.MethodDescriptorProto', MethodDescriptorProto::class)
+    ->optional('name', GPBType::STRING, 1)
+    ->optional('input_type', GPBType::STRING, 2)
+    ->optional('output_type', GPBType::STRING, 3)
+    ->optional('options', GPBType::MESSAGE, 4, 'google.protobuf.internal.MethodOptions')
+    ->optional('client_streaming', GPBType::BOOL, 5)
+    ->optional('server_streaming', GPBType::BOOL, 6)
+    ->finalizeToPool();
+
+$pool->addMessage('google.protobuf.internal.FileOptions', FileOptions::class)
+    ->optional('java_package', GPBType::STRING, 1)
+    ->optional('java_outer_classname', GPBType::STRING, 8)
+    ->optional('java_multiple_files', GPBType::BOOL, 10)
+    ->optional('java_generate_equals_and_hash', GPBType::BOOL, 20)
+    ->optional('java_string_check_utf8', GPBType::BOOL, 27)
+    ->optional('optimize_for', GPBType::ENUM, 9, 'google.protobuf.internal.FileOptions.OptimizeMode')
+    ->optional('go_package', GPBType::STRING, 11)
+    ->optional('cc_generic_services', GPBType::BOOL, 16)
+    ->optional('java_generic_services', GPBType::BOOL, 17)
+    ->optional('py_generic_services', GPBType::BOOL, 18)
+    ->optional('deprecated', GPBType::BOOL, 23)
+    ->optional('cc_enable_arenas', GPBType::BOOL, 31)
+    ->optional('objc_class_prefix', GPBType::STRING, 36)
+    ->optional('csharp_namespace', GPBType::STRING, 37)
+    ->repeated('uninterpreted_option', GPBType::MESSAGE, 999, 'google.protobuf.internal.UninterpretedOption')
+    ->finalizeToPool();
+
+$pool->addEnum('google.protobuf.internal.FileOptions.OptimizeMode', OptimizeMode::class)
+    ->value("SPEED", 1)
+    ->value("CODE_SIZE", 2)
+    ->value("LITE_RUNTIME", 3)
+    ->finalizeToPool();
+
+$pool->addMessage('google.protobuf.internal.MessageOptions', MessageOptions::class)
+    ->optional('message_set_wire_format', GPBType::BOOL, 1)
+    ->optional('no_standard_descriptor_accessor', GPBType::BOOL, 2)
+    ->optional('deprecated', GPBType::BOOL, 3)
+    ->optional('map_entry', GPBType::BOOL, 7)
+    ->repeated('uninterpreted_option', GPBType::MESSAGE, 999, 'google.protobuf.internal.UninterpretedOption')
+    ->finalizeToPool();
+
+$pool->addMessage('google.protobuf.internal.FieldOptions', FieldOptions::class)
+    ->optional('ctype', GPBType::ENUM, 1, 'google.protobuf.internal.FieldOptions.CType')
+    ->optional('packed', GPBType::BOOL, 2)
+    ->optional('jstype', GPBType::ENUM, 6, 'google.protobuf.internal.FieldOptions.JSType')
+    ->optional('lazy', GPBType::BOOL, 5)
+    ->optional('deprecated', GPBType::BOOL, 3)
+    ->optional('weak', GPBType::BOOL, 10)
+    ->repeated('uninterpreted_option', GPBType::MESSAGE, 999, 'google.protobuf.internal.UninterpretedOption')
+    ->finalizeToPool();
+
+$pool->addEnum('google.protobuf.internal.FieldOptions.CType', CType::class)
+    ->value("STRING", 0)
+    ->value("CORD", 1)
+    ->value("STRING_PIECE", 2)
+    ->finalizeToPool();
+
+$pool->addEnum('google.protobuf.internal.FieldOptions.JSType', JSType::class)
+    ->value("JS_NORMAL", 0)
+    ->value("JS_STRING", 1)
+    ->value("JS_NUMBER", 2)
+    ->finalizeToPool();
+
+$pool->addMessage('google.protobuf.internal.OneofOptions', OneofOptions::class)
+    ->repeated('uninterpreted_option', GPBType::MESSAGE, 999, 'google.protobuf.internal.UninterpretedOption')
+    ->finalizeToPool();
+
+$pool->addMessage('google.protobuf.internal.EnumOptions', EnumOptions::class)
+    ->optional('allow_alias', GPBType::BOOL, 2)
+    ->optional('deprecated', GPBType::BOOL, 3)
+    ->repeated('uninterpreted_option', GPBType::MESSAGE, 999, 'google.protobuf.internal.UninterpretedOption')
+    ->finalizeToPool();
+
+$pool->addMessage('google.protobuf.internal.EnumValueOptions', EnumValueOptions::class)
+    ->optional('deprecated', GPBType::BOOL, 1)
+    ->repeated('uninterpreted_option', GPBType::MESSAGE, 999, 'google.protobuf.internal.UninterpretedOption')
+    ->finalizeToPool();
+
+$pool->addMessage('google.protobuf.internal.ServiceOptions', ServiceOptions::class)
+    ->optional('deprecated', GPBType::BOOL, 33)
+    ->repeated('uninterpreted_option', GPBType::MESSAGE, 999, 'google.protobuf.internal.UninterpretedOption')
+    ->finalizeToPool();
+
+$pool->addMessage('google.protobuf.internal.MethodOptions', MethodOptions::class)
+    ->optional('deprecated', GPBType::BOOL, 33)
+    ->repeated('uninterpreted_option', GPBType::MESSAGE, 999, 'google.protobuf.internal.UninterpretedOption')
+    ->finalizeToPool();
+
+$pool->addMessage('google.protobuf.internal.UninterpretedOption', UninterpretedOption::class)
+    ->repeated('name', GPBType::MESSAGE, 2, 'google.protobuf.internal.UninterpretedOption.NamePart')
+    ->optional('identifier_value', GPBType::STRING, 3)
+    ->optional('positive_int_value', GPBType::UINT64, 4)
+    ->optional('negative_int_value', GPBType::INT64, 5)
+    ->optional('double_value', GPBType::DOUBLE, 6)
+    ->optional('string_value', GPBType::BYTES, 7)
+    ->optional('aggregate_value', GPBType::STRING, 8)
+    ->finalizeToPool();
+
+$pool->addMessage('google.protobuf.internal.UninterpretedOption.NamePart', UninterpretedOption_NamePart::class)
+    ->required('name_part', GPBType::STRING, 1)
+    ->required('is_extension', GPBType::BOOL, 2)
+    ->finalizeToPool();
+
+$pool->addMessage('google.protobuf.internal.SourceCodeInfo', SourceCodeInfo::class)
+    ->repeated('location', GPBType::MESSAGE, 1, 'google.protobuf.internal.SourceCodeInfo.Location')
+    ->finalizeToPool();
+
+$pool->addMessage('google.protobuf.internal.SourceCodeInfo.Location', SourceCodeInfo_Location::class)
+    ->repeated('path', GPBType::INT32, 1)
+    ->repeated('span', GPBType::INT32, 2)
+    ->optional('leading_comments', GPBType::STRING, 3)
+    ->optional('trailing_comments', GPBType::STRING, 4)
+    ->repeated('leading_detached_comments', GPBType::STRING, 6)
+    ->finalizeToPool();
+
+$pool->addMessage('google.protobuf.internal.GeneratedCodeInfo', GeneratedCodeInfo::class)
+    ->repeated('annotation', GPBType::MESSAGE, 1, 'google.protobuf.internal.GeneratedCodeInfo.Annotation')
+    ->finalizeToPool();
+
+$pool->addMessage('google.protobuf.internal.GeneratedCodeInfo.Annotation', GeneratedCodeInfo_Annotation::class)
+    ->repeated('path', GPBType::INT32, 1)
+    ->optional('source_file', GPBType::STRING, 2)
+    ->optional('begin', GPBType::INT32, 3)
+    ->optional('end', GPBType::INT32, 4)
+    ->finalizeToPool();
+
+$pool->finish();
diff --git a/php/src/phpdoc.dist.xml b/php/src/phpdoc.dist.xml
new file mode 100644
index 0000000..dd31302
--- /dev/null
+++ b/php/src/phpdoc.dist.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- Install phpDocumentor to generate docs. -->
+<phpdoc>
+  <parser>
+    <target>doc</target>
+  </parser>
+  <transformer>
+    <target>doc</target>
+  </transformer>
+  <files>
+    <file>Google/Protobuf/Internal/MapField.php</file>
+    <file>Google/Protobuf/Internal/Message.php</file>
+    <file>Google/Protobuf/Internal/RepeatedField.php</file>
+  </files>
+</phpdoc>
diff --git a/php/tests/array_test.php b/php/tests/array_test.php
new file mode 100644
index 0000000..d683add
--- /dev/null
+++ b/php/tests/array_test.php
@@ -0,0 +1,888 @@
+<?php
+
+require_once('test.pb.php');
+require_once('test_util.php');
+
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBType;
+use Foo\TestMessage;
+use Foo\TestMessage_Sub;
+
+class RepeatedFieldTest extends PHPUnit_Framework_TestCase
+{
+
+    #########################################################
+    # Test int32 field.
+    #########################################################
+
+    public function testInt32()
+    {
+        $arr = new RepeatedField(GPBType::INT32);
+
+        // Test append.
+        $arr []= MAX_INT32;
+        $this->assertSame(MAX_INT32, $arr[0]);
+        $arr []= MIN_INT32;
+        $this->assertSame(MIN_INT32, $arr[1]);
+
+        $arr []= 1.1;
+        $this->assertSame(1, $arr[2]);
+        $arr []= MAX_INT32_FLOAT;
+        $this->assertSame(MAX_INT32, $arr[3]);
+        $arr []= MAX_INT32_FLOAT;
+        $this->assertSame(MAX_INT32, $arr[4]);
+
+        $arr []= '2';
+        $this->assertSame(2, $arr[5]);
+        $arr []= '3.1';
+        $this->assertSame(3, $arr[6]);
+        $arr []= MAX_INT32_STRING;
+        $this->assertSame(MAX_INT32, $arr[7]);
+
+        $this->assertEquals(8, count($arr));
+
+        for ($i = 0; $i < count($arr); $i++) {
+            $arr[$i] = 0;
+            $this->assertSame(0, $arr[$i]);
+        }
+
+        // Test set.
+        $arr [0]= MAX_INT32;
+        $this->assertSame(MAX_INT32, $arr[0]);
+        $arr [1]= MIN_INT32;
+        $this->assertSame(MIN_INT32, $arr[1]);
+
+        $arr [2]= 1.1;
+        $this->assertSame(1, $arr[2]);
+        $arr [3]= MAX_INT32_FLOAT;
+        $this->assertSame(MAX_INT32, $arr[3]);
+        $arr [4]= MAX_INT32_FLOAT;
+        $this->assertSame(MAX_INT32, $arr[4]);
+
+        $arr [5]= '2';
+        $this->assertSame(2, $arr[5]);
+        $arr [6]= '3.1';
+        $this->assertSame(3, $arr[6]);
+        $arr [7]= MAX_INT32_STRING;
+        $this->assertSame(MAX_INT32, $arr[7]);
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testInt32AppendStringFail()
+    {
+        $arr = new RepeatedField(GPBType::INT32);
+        $arr []= 'abc';
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testInt32SetStringFail()
+    {
+        $arr = new RepeatedField(GPBType::INT32);
+        $arr []= 0;
+        $arr [0]= 'abc';
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testInt32AppendMessageFail()
+    {
+        $arr = new RepeatedField(GPBType::INT32);
+        $arr []= new TestMessage_Sub();
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testInt32SetMessageFail()
+    {
+        $arr = new RepeatedField(GPBType::INT32);
+        $arr []= 0;
+        $arr [0]= new TestMessage_Sub();
+    }
+
+    #########################################################
+    # Test uint32 field.
+    #########################################################
+
+    public function testUint32()
+    {
+        $arr = new RepeatedField(GPBType::UINT32);
+
+        // Test append.
+        $arr []= MAX_UINT32;
+        $this->assertSame(-1, $arr[0]);
+        $arr []= -1;
+        $this->assertSame(-1, $arr[1]);
+        $arr []= MIN_UINT32;
+        $this->assertSame(MIN_UINT32, $arr[2]);
+
+        $arr []= 1.1;
+        $this->assertSame(1, $arr[3]);
+        $arr []= MAX_UINT32_FLOAT;
+        $this->assertSame(-1, $arr[4]);
+        $arr []= -1.0;
+        $this->assertSame(-1, $arr[5]);
+        $arr []= MIN_UINT32_FLOAT;
+        $this->assertSame(MIN_UINT32, $arr[6]);
+
+        $arr []= '2';
+        $this->assertSame(2, $arr[7]);
+        $arr []= '3.1';
+        $this->assertSame(3, $arr[8]);
+        $arr []= MAX_UINT32_STRING;
+        $this->assertSame(-1, $arr[9]);
+        $arr []= '-1.0';
+        $this->assertSame(-1, $arr[10]);
+        $arr []= MIN_UINT32_STRING;
+        $this->assertSame(MIN_UINT32, $arr[11]);
+
+        $this->assertEquals(12, count($arr));
+
+        for ($i = 0; $i < count($arr); $i++) {
+            $arr[$i] = 0;
+            $this->assertSame(0, $arr[$i]);
+        }
+
+        // Test set.
+        $arr [0]= MAX_UINT32;
+        $this->assertSame(-1, $arr[0]);
+        $arr [1]= -1;
+        $this->assertSame(-1, $arr[1]);
+        $arr [2]= MIN_UINT32;
+        $this->assertSame(MIN_UINT32, $arr[2]);
+
+        $arr [3]= 1.1;
+        $this->assertSame(1, $arr[3]);
+        $arr [4]= MAX_UINT32_FLOAT;
+        $this->assertSame(-1, $arr[4]);
+        $arr [5]= -1.0;
+        $this->assertSame(-1, $arr[5]);
+        $arr [6]= MIN_UINT32_FLOAT;
+        $this->assertSame(MIN_UINT32, $arr[6]);
+
+        $arr [7]= '2';
+        $this->assertSame(2, $arr[7]);
+        $arr [8]= '3.1';
+        $this->assertSame(3, $arr[8]);
+        $arr [9]= MAX_UINT32_STRING;
+        $this->assertSame(-1, $arr[9]);
+        $arr [10]= '-1.0';
+        $this->assertSame(-1, $arr[10]);
+        $arr [11]= MIN_UINT32_STRING;
+        $this->assertSame(MIN_UINT32, $arr[11]);
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testUint32AppendStringFail()
+    {
+        $arr = new RepeatedField(GPBType::UINT32);
+        $arr []= 'abc';
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testUint32SetStringFail()
+    {
+        $arr = new RepeatedField(GPBType::UINT32);
+        $arr []= 0;
+        $arr [0]= 'abc';
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testUint32AppendMessageFail()
+    {
+        $arr = new RepeatedField(GPBType::UINT32);
+        $arr []= new TestMessage_Sub();
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testUint32SetMessageFail()
+    {
+        $arr = new RepeatedField(GPBType::UINT32);
+        $arr []= 0;
+        $arr [0]= new TestMessage_Sub();
+    }
+
+    #########################################################
+    # Test int64 field.
+    #########################################################
+
+    public function testInt64()
+    {
+        $arr = new RepeatedField(GPBType::INT64);
+
+        // Test append.
+        $arr []= MAX_INT64;
+        $this->assertSame(MAX_INT64, $arr[0]);
+        $arr []= MIN_INT64;
+        $this->assertEquals(MIN_INT64, $arr[1]);
+
+        $arr []= 1.1;
+        $this->assertSame(1, $arr[2]);
+
+        $arr []= '2';
+        $this->assertSame(2, $arr[3]);
+        $arr []= '3.1';
+        $this->assertSame(3, $arr[4]);
+        $arr []= MAX_INT64_STRING;
+        $this->assertSame(MAX_INT64, $arr[5]);
+        $arr []= MIN_INT64_STRING;
+        $this->assertEquals(MIN_INT64, $arr[6]);
+
+        $this->assertEquals(7, count($arr));
+
+        for ($i = 0; $i < count($arr); $i++) {
+            $arr[$i] = 0;
+            $this->assertSame(0, $arr[$i]);
+        }
+
+        // Test set.
+        $arr [0]= MAX_INT64;
+        $this->assertSame(MAX_INT64, $arr[0]);
+        $arr [1]= MIN_INT64;
+        $this->assertEquals(MIN_INT64, $arr[1]);
+
+        $arr [2]= 1.1;
+        $this->assertSame(1, $arr[2]);
+
+        $arr [3]= '2';
+        $this->assertSame(2, $arr[3]);
+        $arr [4]= '3.1';
+        $this->assertSame(3, $arr[4]);
+        $arr [5]= MAX_INT64_STRING;
+        $this->assertSame(MAX_INT64, $arr[5]);
+        $arr [6]= MIN_INT64_STRING;
+        $this->assertEquals(MIN_INT64, $arr[6]);
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testInt64AppendStringFail()
+    {
+        $arr = new RepeatedField(GPBType::INT64);
+        $arr []= 'abc';
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testInt64SetStringFail()
+    {
+        $arr = new RepeatedField(GPBType::INT64);
+        $arr []= 0;
+        $arr [0]= 'abc';
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testInt64AppendMessageFail()
+    {
+        $arr = new RepeatedField(GPBType::INT64);
+        $arr []= new TestMessage_Sub();
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testInt64SetMessageFail()
+    {
+        $arr = new RepeatedField(GPBType::INT64);
+        $arr []= 0;
+        $arr [0]= new TestMessage_Sub();
+    }
+
+    #########################################################
+    # Test uint64 field.
+    #########################################################
+
+    public function testUint64()
+    {
+        $arr = new RepeatedField(GPBType::UINT64);
+
+        // Test append.
+        $arr []= MAX_UINT64;
+        $this->assertEquals(MAX_UINT64, $arr[0]);
+
+        $arr []= 1.1;
+        $this->assertSame(1, $arr[1]);
+
+        $arr []= '2';
+        $this->assertSame(2, $arr[2]);
+        $arr []= '3.1';
+        $this->assertSame(3, $arr[3]);
+        $arr []= MAX_UINT64_STRING;
+        $this->assertEquals(MAX_UINT64, $arr[4]);
+
+        $this->assertEquals(5, count($arr));
+
+        for ($i = 0; $i < count($arr); $i++) {
+            $arr[$i] = 0;
+            $this->assertSame(0, $arr[$i]);
+        }
+
+        // Test set.
+        $arr [0]= MAX_UINT64;
+        $this->assertEquals(MAX_UINT64, $arr[0]);
+
+        $arr [1]= 1.1;
+        $this->assertSame(1, $arr[1]);
+
+        $arr [2]= '2';
+        $this->assertSame(2, $arr[2]);
+        $arr [3]= '3.1';
+        $this->assertSame(3, $arr[3]);
+        $arr [4]= MAX_UINT64_STRING;
+        $this->assertEquals(MAX_UINT64, $arr[4]);
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testUint64AppendStringFail()
+    {
+        $arr = new RepeatedField(GPBType::UINT64);
+        $arr []= 'abc';
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testUint64SetStringFail()
+    {
+        $arr = new RepeatedField(GPBType::UINT64);
+        $arr []= 0;
+        $arr [0]= 'abc';
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testUint64AppendMessageFail()
+    {
+        $arr = new RepeatedField(GPBType::UINT64);
+        $arr []= new TestMessage_Sub();
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testUint64SetMessageFail()
+    {
+        $arr = new RepeatedField(GPBType::UINT64);
+        $arr []= 0;
+        $arr [0]= new TestMessage_Sub();
+    }
+
+    #########################################################
+    # Test float field.
+    #########################################################
+
+    public function testFloat()
+    {
+        $arr = new RepeatedField(GPBType::FLOAT);
+
+        // Test append.
+        $arr []= 1;
+        $this->assertEquals(1.0, $arr[0], '', MAX_FLOAT_DIFF);
+
+        $arr []= 1.1;
+        $this->assertEquals(1.1, $arr[1], '', MAX_FLOAT_DIFF);
+
+        $arr []= '2';
+        $this->assertEquals(2.0, $arr[2], '', MAX_FLOAT_DIFF);
+        $arr []= '3.1';
+        $this->assertEquals(3.1, $arr[3], '', MAX_FLOAT_DIFF);
+
+        $this->assertEquals(4, count($arr));
+
+        for ($i = 0; $i < count($arr); $i++) {
+            $arr[$i] = 0;
+            $this->assertSame(0.0, $arr[$i]);
+        }
+
+        // Test set.
+        $arr [0]= 1;
+        $this->assertEquals(1.0, $arr[0], '', MAX_FLOAT_DIFF);
+
+        $arr [1]= 1.1;
+        $this->assertEquals(1.1, $arr[1], '', MAX_FLOAT_DIFF);
+
+        $arr [2]= '2';
+        $this->assertEquals(2.0, $arr[2], '', MAX_FLOAT_DIFF);
+        $arr [3]= '3.1';
+        $this->assertEquals(3.1, $arr[3], '', MAX_FLOAT_DIFF);
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testFloatAppendStringFail()
+    {
+        $arr = new RepeatedField(GPBType::FLOAT);
+        $arr []= 'abc';
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testFloatSetStringFail()
+    {
+        $arr = new RepeatedField(GPBType::FLOAT);
+        $arr []= 0.0;
+        $arr [0]= 'abc';
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testFloatAppendMessageFail()
+    {
+        $arr = new RepeatedField(GPBType::FLOAT);
+        $arr []= new TestMessage_Sub();
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testFloatSetMessageFail()
+    {
+        $arr = new RepeatedField(GPBType::FLOAT);
+        $arr []= 0.0;
+        $arr [0]= new TestMessage_Sub();
+    }
+
+    #########################################################
+    # Test double field.
+    #########################################################
+
+    public function testDouble()
+    {
+        $arr = new RepeatedField(GPBType::DOUBLE);
+
+        // Test append.
+        $arr []= 1;
+        $this->assertEquals(1.0, $arr[0], '', MAX_FLOAT_DIFF);
+
+        $arr []= 1.1;
+        $this->assertEquals(1.1, $arr[1], '', MAX_FLOAT_DIFF);
+
+        $arr []= '2';
+        $this->assertEquals(2.0, $arr[2], '', MAX_FLOAT_DIFF);
+        $arr []= '3.1';
+        $this->assertEquals(3.1, $arr[3], '', MAX_FLOAT_DIFF);
+
+        $this->assertEquals(4, count($arr));
+
+        for ($i = 0; $i < count($arr); $i++) {
+            $arr[$i] = 0;
+            $this->assertSame(0.0, $arr[$i]);
+        }
+
+        // Test set.
+        $arr [0]= 1;
+        $this->assertEquals(1.0, $arr[0], '', MAX_FLOAT_DIFF);
+
+        $arr [1]= 1.1;
+        $this->assertEquals(1.1, $arr[1], '', MAX_FLOAT_DIFF);
+
+        $arr [2]= '2';
+        $this->assertEquals(2.0, $arr[2], '', MAX_FLOAT_DIFF);
+        $arr [3]= '3.1';
+        $this->assertEquals(3.1, $arr[3], '', MAX_FLOAT_DIFF);
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testDoubleAppendStringFail()
+    {
+        $arr = new RepeatedField(GPBType::DOUBLE);
+        $arr []= 'abc';
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testDoubleSetStringFail()
+    {
+        $arr = new RepeatedField(GPBType::DOUBLE);
+        $arr []= 0.0;
+        $arr [0]= 'abc';
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testDoubleAppendMessageFail()
+    {
+        $arr = new RepeatedField(GPBType::DOUBLE);
+        $arr []= new TestMessage_Sub();
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testDoubleSetMessageFail()
+    {
+        $arr = new RepeatedField(GPBType::DOUBLE);
+        $arr []= 0.0;
+        $arr [0]= new TestMessage_Sub();
+    }
+
+    #########################################################
+    # Test bool field.
+    #########################################################
+
+    public function testBool()
+    {
+        $arr = new RepeatedField(GPBType::BOOL);
+
+        // Test append.
+        $arr []= true;
+        $this->assertSame(true, $arr[0]);
+
+        $arr []= -1;
+        $this->assertSame(true, $arr[1]);
+
+        $arr []= 1.1;
+        $this->assertSame(true, $arr[2]);
+
+        $arr []= '';
+        $this->assertSame(false, $arr[3]);
+
+        $this->assertEquals(4, count($arr));
+
+        for ($i = 0; $i < count($arr); $i++) {
+            $arr[$i] = 0;
+            $this->assertSame(false, $arr[$i]);
+        }
+
+        // Test set.
+        $arr [0]= true;
+        $this->assertSame(true, $arr[0]);
+
+        $arr [1]= -1;
+        $this->assertSame(true, $arr[1]);
+
+        $arr [2]= 1.1;
+        $this->assertSame(true, $arr[2]);
+
+        $arr [3]= '';
+        $this->assertSame(false, $arr[3]);
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testBoolAppendMessageFail()
+    {
+        $arr = new RepeatedField(GPBType::BOOL);
+        $arr []= new TestMessage_Sub();
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testBoolSetMessageFail()
+    {
+        $arr = new RepeatedField(GPBType::BOOL);
+        $arr []= true;
+        $arr [0]= new TestMessage_Sub();
+    }
+
+    #########################################################
+    # Test string field.
+    #########################################################
+
+    public function testString()
+    {
+        $arr = new RepeatedField(GPBType::STRING);
+
+        // Test append.
+        $arr []= 'abc';
+        $this->assertSame('abc', $arr[0]);
+
+        $arr []= 1;
+        $this->assertSame('1', $arr[1]);
+
+        $arr []= 1.1;
+        $this->assertSame('1.1', $arr[2]);
+
+        $arr []= true;
+        $this->assertSame('1', $arr[3]);
+
+        $this->assertEquals(4, count($arr));
+
+        for ($i = 0; $i < count($arr); $i++) {
+            $arr[$i] = '';
+            $this->assertSame('', $arr[$i]);
+        }
+
+        // Test set.
+        $arr [0]= 'abc';
+        $this->assertSame('abc', $arr[0]);
+
+        $arr [1]= 1;
+        $this->assertSame('1', $arr[1]);
+
+        $arr [2]= 1.1;
+        $this->assertSame('1.1', $arr[2]);
+
+        $arr [3]= true;
+        $this->assertSame('1', $arr[3]);
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testStringAppendMessageFail()
+    {
+        $arr = new RepeatedField(GPBType::STRING);
+        $arr []= new TestMessage_Sub();
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testStringSetMessageFail()
+    {
+        $arr = new RepeatedField(GPBType::STRING);
+        $arr []= 'abc';
+        $arr [0]= new TestMessage_Sub();
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testStringAppendInvalidUTF8Fail()
+    {
+        $arr = new RepeatedField(GPBType::STRING);
+        $hex = hex2bin("ff");
+        $arr []= $hex;
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testStringSetInvalidUTF8Fail()
+    {
+        $arr = new RepeatedField(GPBType::STRING);
+        $arr []= 'abc';
+        $hex = hex2bin("ff");
+        $arr [0]= $hex;
+    }
+
+    #########################################################
+    # Test message field.
+    #########################################################
+
+    public function testMessage()
+    {
+        $arr = new RepeatedField(GPBType::MESSAGE, TestMessage_Sub::class);
+
+        // Test append.
+        $sub_m = new TestMessage_Sub();
+        $sub_m->setA(1);
+        $arr []= $sub_m;
+        $this->assertSame(1, $arr[0]->getA());
+
+        $null = null;
+        $arr []= $null;
+        $this->assertNull($arr[1]);
+
+        $this->assertEquals(2, count($arr));
+
+        for ($i = 0; $i < count($arr); $i++) {
+            $arr[$i] = $null;
+            $this->assertNull($arr[$i]);
+        }
+
+        // Test set.
+        $arr [0]= $sub_m;
+        $this->assertSame(1, $arr[0]->getA());
+
+        $arr [1]= $null;
+        $this->assertNull($arr[1]);
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testMessageAppendIntFail()
+    {
+        $arr = new RepeatedField(GPBType::MESSAGE, TestMessage_Sub::class);
+        $arr []= 1;
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testMessageSetIntFail()
+    {
+        $arr = new RepeatedField(GPBType::MESSAGE, TestMessage_Sub::class);
+        $arr []= new TestMessage_Sub;
+        $arr [0]= 'abc';
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testMessageAppendStringFail()
+    {
+        $arr = new RepeatedField(GPBType::MESSAGE, TestMessage_Sub::class);
+        $arr []= 'abc';
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testMessageSetStringFail()
+    {
+        $arr = new RepeatedField(GPBType::MESSAGE, TestMessage_Sub::class);
+        $arr []= new TestMessage_Sub;
+        $arr [0]= 'abc';
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testMessageAppendOtherMessageFail()
+    {
+        $arr = new RepeatedField(GPBType::MESSAGE, TestMessage_Sub::class);
+        $arr []= new TestMessage;
+    }
+
+    #########################################################
+    # Test offset type
+    #########################################################
+
+    public function testOffset()
+    {
+        $arr = new RepeatedField(GPBType::INT32);
+        $arr []= 0;
+
+        $arr [0]= 1;
+        $this->assertSame(1, $arr[0]);
+        $this->assertSame(1, count($arr));
+
+        $arr ['0']= 2;
+        $this->assertSame(2, $arr['0']);
+        $this->assertSame(2, $arr[0]);
+        $this->assertSame(1, count($arr));
+
+        $arr [0.0]= 3;
+        $this->assertSame(3, $arr[0.0]);
+        $this->assertSame(1, count($arr));
+    }
+
+      public function testInsertRemoval()
+      {
+          $arr = new RepeatedField(GPBType::INT32);
+
+          $arr []= 0;
+          $arr []= 1;
+          $arr []= 2;
+          $this->assertSame(3, count($arr));
+
+          unset($arr[2]);
+          $this->assertSame(2, count($arr));
+          $this->assertSame(0, $arr[0]);
+          $this->assertSame(1, $arr[1]);
+
+          $arr [] = 3;
+          $this->assertSame(3, count($arr));
+          $this->assertSame(0, $arr[0]);
+          $this->assertSame(1, $arr[1]);
+          $this->assertSame(3, $arr[2]);
+      }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testRemoveMiddleFail()
+    {
+        $arr = new RepeatedField(GPBType::INT32);
+
+        $arr []= 0;
+        $arr []= 1;
+        $arr []= 2;
+        $this->assertSame(3, count($arr));
+
+        unset($arr[1]);
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testRemoveEmptyFail()
+    {
+        $arr = new RepeatedField(GPBType::INT32);
+
+        unset($arr[0]);
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testMessageOffsetFail()
+    {
+        $arr = new RepeatedField(GPBType::INT32);
+        $arr []= 0;
+        $arr [new TestMessage_Sub()]= 0;
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testStringOffsetFail()
+    {
+        $arr = new RepeatedField(GPBType::INT32);
+        $arr []= 0;
+        $arr ['abc']= 0;
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testSetNonExistedOffsetFail()
+    {
+        $arr = new RepeatedField(GPBType::INT32);
+        $arr [0]= 0;
+    }
+
+    #########################################################
+    # Test memory leak
+    #########################################################
+
+    public function testCycleLeak()
+    {
+        $arr = new RepeatedField(GPBType::MESSAGE, TestMessage::class);
+        $arr []= new TestMessage;
+        $arr[0]->SetRepeatedRecursive($arr);
+
+        // Clean up memory before test.
+        gc_collect_cycles();
+        $start = memory_get_usage();
+        unset($arr);
+
+        // Explicitly trigger garbage collection.
+        gc_collect_cycles();
+
+        $end = memory_get_usage();
+        $this->assertLessThan($start, $end);
+    }
+}
diff --git a/php/tests/autoload.php b/php/tests/autoload.php
deleted file mode 100644
index af88ba0..0000000
--- a/php/tests/autoload.php
+++ /dev/null
@@ -1,4 +0,0 @@
-<?php
-
-require_once('test.pb.php');
-require_once('test_util.php');
diff --git a/php/tests/encode_decode_test.php b/php/tests/encode_decode_test.php
new file mode 100644
index 0000000..d07907c
--- /dev/null
+++ b/php/tests/encode_decode_test.php
@@ -0,0 +1,136 @@
+<?php
+
+require_once('test.pb.php');
+require_once('test_base.php');
+require_once('test_util.php');
+
+use Google\Protobuf\RepeatedField;
+use Google\Protobuf\GPBType;
+use Foo\TestEnum;
+use Foo\TestMessage;
+use Foo\TestMessage_Sub;
+use Foo\TestPackedMessage;
+use Foo\TestUnpackedMessage;
+
+class EncodeDecodeTest extends TestBase
+{
+
+    public function testEncode()
+    {
+        $from = new TestMessage();
+        $this->expectEmptyFields($from);
+        $this->setFields($from);
+        $this->expectFields($from);
+
+        $data = $from->encode();
+        $this->assertSame(TestUtil::getGoldenTestMessage(), $data);
+    }
+
+    public function testDecode()
+    {
+        $to = new TestMessage();
+        $to->decode(TestUtil::getGoldenTestMessage());
+        $this->expectFields($to);
+    }
+
+    public function testEncodeDecode()
+    {
+        $from = new TestMessage();
+        $this->expectEmptyFields($from);
+        $this->setFields($from);
+        $this->expectFields($from);
+
+        $data = $from->encode();
+
+        $to = new TestMessage();
+        $to->decode($data);
+        $this->expectFields($to);
+    }
+
+    public function testEncodeDecodeEmpty()
+    {
+        $from = new TestMessage();
+        $this->expectEmptyFields($from);
+
+        $data = $from->encode();
+
+        $to = new TestMessage();
+        $to->decode($data);
+        $this->expectEmptyFields($to);
+    }
+
+    public function testEncodeDecodeOneof()
+    {
+        $m = new TestMessage();
+
+        $m->setOneofInt32(1);
+        $data = $m->encode();
+        $n = new TestMessage();
+        $n->decode($data);
+        $this->assertSame(1, $n->getOneofInt32());
+
+        $m->setOneofFloat(2.0);
+        $data = $m->encode();
+        $n = new TestMessage();
+        $n->decode($data);
+        $this->assertSame(2.0, $n->getOneofFloat());
+
+        $m->setOneofString('abc');
+        $data = $m->encode();
+        $n = new TestMessage();
+        $n->decode($data);
+        $this->assertSame('abc', $n->getOneofString());
+
+        $sub_m = new TestMessage_Sub();
+        $sub_m->setA(1);
+        $m->setOneofMessage($sub_m);
+        $data = $m->encode();
+        $n = new TestMessage();
+        $n->decode($data);
+        $this->assertSame(1, $n->getOneofMessage()->getA());
+    }
+
+    public function testPackedEncode()
+    {
+        $from = new TestPackedMessage();
+        TestUtil::setTestPackedMessage($from);
+        $this->assertSame(TestUtil::getGoldenTestPackedMessage(),
+                          $from->encode());
+    }
+
+    public function testPackedDecodePacked()
+    {
+        $to = new TestPackedMessage();
+        $to->decode(TestUtil::getGoldenTestPackedMessage());
+        TestUtil::assertTestPackedMessage($to);
+    }
+
+    public function testPackedDecodeUnpacked()
+    {
+        $to = new TestPackedMessage();
+        $to->decode(TestUtil::getGoldenTestUnpackedMessage());
+        TestUtil::assertTestPackedMessage($to);
+    }
+
+    public function testUnpackedEncode()
+    {
+        $from = new TestUnpackedMessage();
+        TestUtil::setTestPackedMessage($from);
+        $this->assertSame(TestUtil::getGoldenTestUnpackedMessage(),
+                          $from->encode());
+    }
+
+    public function testUnpackedDecodePacked()
+    {
+        $to = new TestUnpackedMessage();
+        $to->decode(TestUtil::getGoldenTestPackedMessage());
+        TestUtil::assertTestPackedMessage($to);
+    }
+
+    public function testUnpackedDecodeUnpacked()
+    {
+        $to = new TestUnpackedMessage();
+        $to->decode(TestUtil::getGoldenTestUnpackedMessage());
+        TestUtil::assertTestPackedMessage($to);
+    }
+}
diff --git a/php/tests/generated_class_test.php b/php/tests/generated_class_test.php
new file mode 100644
index 0000000..56466ca
--- /dev/null
+++ b/php/tests/generated_class_test.php
@@ -0,0 +1,557 @@
+<?php
+
+require_once('test.pb.php');
+require_once('test_util.php');
+
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBType;
+use Foo\TestEnum;
+use Foo\TestMessage;
+use Foo\TestMessage_Sub;
+
+class GeneratedClassTest extends PHPUnit_Framework_TestCase
+{
+
+    #########################################################
+    # Test field accessors.
+    #########################################################
+
+    public function testSetterGetter()
+    {
+        $m = new TestMessage();
+        $m->setOptionalInt32(1);
+        $this->assertSame(1, $m->getOptionalInt32());
+    }
+
+    #########################################################
+    # Test int32 field.
+    #########################################################
+
+    public function testInt32Field()
+    {
+        $m = new TestMessage();
+
+        // Set integer.
+        $m->setOptionalInt32(MAX_INT32);
+        $this->assertSame(MAX_INT32, $m->getOptionalInt32());
+        $m->setOptionalInt32(MIN_INT32);
+        $this->assertSame(MIN_INT32, $m->getOptionalInt32());
+
+        // Set float.
+        $m->setOptionalInt32(1.1);
+        $this->assertSame(1, $m->getOptionalInt32());
+        $m->setOptionalInt32(MAX_INT32_FLOAT);
+        $this->assertSame(MAX_INT32, $m->getOptionalInt32());
+        $m->setOptionalInt32(MIN_INT32_FLOAT);
+        $this->assertSame(MIN_INT32, $m->getOptionalInt32());
+
+        // Set string.
+        $m->setOptionalInt32('2');
+        $this->assertSame(2, $m->getOptionalInt32());
+        $m->setOptionalInt32('3.1');
+        $this->assertSame(3, $m->getOptionalInt32());
+        $m->setOptionalInt32(MAX_INT32_STRING);
+        $this->assertSame(MAX_INT32, $m->getOptionalInt32());
+        $m->setOptionalInt32(MIN_INT32_STRING);
+        $this->assertSame(MIN_INT32, $m->getOptionalInt32());
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testInt32FieldInvalidTypeFail()
+    {
+        $m = new TestMessage();
+        $m->setOptionalInt32(new TestMessage());
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testInt32FieldInvalidStringFail()
+    {
+        $m = new TestMessage();
+        $m->setOptionalInt32('abc');
+    }
+
+    #########################################################
+    # Test uint32 field.
+    #########################################################
+
+    public function testUint32Field()
+    {
+        $m = new TestMessage();
+
+        // Set integer.
+        $m->setOptionalUint32(MAX_UINT32);
+        $this->assertSame(-1, $m->getOptionalUint32());
+        $m->setOptionalUint32(-1);
+        $this->assertSame(-1, $m->getOptionalUint32());
+        $m->setOptionalUint32(MIN_UINT32);
+        $this->assertSame(MIN_INT32, $m->getOptionalUint32());
+
+        // Set float.
+        $m->setOptionalUint32(1.1);
+        $this->assertSame(1, $m->getOptionalUint32());
+        $m->setOptionalUint32(MAX_UINT32_FLOAT);
+        $this->assertSame(-1, $m->getOptionalUint32());
+        $m->setOptionalUint32(-1.0);
+        $this->assertSame(-1, $m->getOptionalUint32());
+        $m->setOptionalUint32(MIN_UINT32_FLOAT);
+        $this->assertSame(MIN_INT32, $m->getOptionalUint32());
+
+        // Set string.
+        $m->setOptionalUint32('2');
+        $this->assertSame(2, $m->getOptionalUint32());
+        $m->setOptionalUint32('3.1');
+        $this->assertSame(3, $m->getOptionalUint32());
+        $m->setOptionalUint32(MAX_UINT32_STRING);
+        $this->assertSame(-1, $m->getOptionalUint32());
+        $m->setOptionalUint32('-1.0');
+        $this->assertSame(-1, $m->getOptionalUint32());
+        $m->setOptionalUint32(MIN_UINT32_STRING);
+        $this->assertSame(MIN_INT32, $m->getOptionalUint32());
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testUint32FieldInvalidTypeFail()
+    {
+        $m = new TestMessage();
+        $m->setOptionalUint32(new TestMessage());
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testUint32FieldInvalidStringFail()
+    {
+        $m = new TestMessage();
+        $m->setOptionalUint32('abc');
+    }
+
+    #########################################################
+    # Test int64 field.
+    #########################################################
+
+    public function testInt64Field()
+    {
+        $m = new TestMessage();
+
+        // Set integer.
+        $m->setOptionalInt64(MAX_INT64);
+        $this->assertSame(MAX_INT64, $m->getOptionalInt64());
+        $m->setOptionalInt64(MIN_INT64);
+        $this->assertEquals(MIN_INT64, $m->getOptionalInt64());
+
+        // Set float.
+        $m->setOptionalInt64(1.1);
+        $this->assertSame(1, $m->getOptionalInt64());
+
+        // Set string.
+        $m->setOptionalInt64('2');
+        $this->assertSame(2, $m->getOptionalInt64());
+        $m->setOptionalInt64('3.1');
+        $this->assertSame(3, $m->getOptionalInt64());
+        $m->setOptionalInt64(MAX_INT64_STRING);
+        $this->assertSame(MAX_INT64, $m->getOptionalInt64());
+        $m->setOptionalInt64(MIN_INT64_STRING);
+        $this->assertEquals(MIN_INT64, $m->getOptionalInt64());
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testInt64FieldInvalidTypeFail()
+    {
+        $m = new TestMessage();
+        $m->setOptionalInt64(new TestMessage());
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testInt64FieldInvalidStringFail()
+    {
+        $m = new TestMessage();
+        $m->setOptionalInt64('abc');
+    }
+
+    #########################################################
+    # Test uint64 field.
+    #########################################################
+
+    public function testUint64Field()
+    {
+        $m = new TestMessage();
+
+        // Set integer.
+        $m->setOptionalUint64(MAX_UINT64);
+        $this->assertEquals(MAX_UINT64, $m->getOptionalUint64());
+
+        // Set float.
+        $m->setOptionalUint64(1.1);
+        $this->assertSame(1, $m->getOptionalUint64());
+
+        // Set string.
+        $m->setOptionalUint64('2');
+        $this->assertSame(2, $m->getOptionalUint64());
+        $m->setOptionalUint64('3.1');
+        $this->assertSame(3, $m->getOptionalUint64());
+        $m->setOptionalUint64(MAX_UINT64_STRING);
+        $this->assertEquals(MAX_UINT64, $m->getOptionalUint64());
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testUint64FieldInvalidTypeFail()
+    {
+        $m = new TestMessage();
+        $m->setOptionalUint64(new TestMessage());
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testUint64FieldInvalidStringFail()
+    {
+        $m = new TestMessage();
+        $m->setOptionalUint64('abc');
+    }
+
+    #########################################################
+    # Test enum field.
+    #########################################################
+
+    public function testEnumField()
+    {
+        $m = new TestMessage();
+
+        // Set enum.
+        $m->setOptionalEnum(TestEnum::ONE);
+        $this->assertEquals(TestEnum::ONE, $m->getOptionalEnum());
+
+        // Set integer.
+        $m->setOptionalEnum(1);
+        $this->assertEquals(TestEnum::ONE, $m->getOptionalEnum());
+
+        // Set float.
+        $m->setOptionalEnum(1.1);
+        $this->assertEquals(TestEnum::ONE, $m->getOptionalEnum());
+
+        // Set string.
+        $m->setOptionalEnum("1");
+        $this->assertEquals(TestEnum::ONE, $m->getOptionalEnum());
+    }
+
+    #########################################################
+    # Test float field.
+    #########################################################
+
+    public function testFloatField()
+    {
+        $m = new TestMessage();
+
+        // Set integer.
+        $m->setOptionalFloat(1);
+        $this->assertEquals(1.0, $m->getOptionalFloat(), '', MAX_FLOAT_DIFF);
+
+        // Set float.
+        $m->setOptionalFloat(1.1);
+        $this->assertEquals(1.1, $m->getOptionalFloat(), '', MAX_FLOAT_DIFF);
+
+        // Set string.
+        $m->setOptionalFloat('2');
+        $this->assertEquals(2.0, $m->getOptionalFloat(), '', MAX_FLOAT_DIFF);
+        $m->setOptionalFloat('3.1');
+        $this->assertEquals(3.1, $m->getOptionalFloat(), '', MAX_FLOAT_DIFF);
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testFloatFieldInvalidTypeFail()
+    {
+        $m = new TestMessage();
+        $m->setOptionalFloat(new TestMessage());
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testFloatFieldInvalidStringFail()
+    {
+        $m = new TestMessage();
+        $m->setOptionalFloat('abc');
+    }
+
+    #########################################################
+    # Test double field.
+    #########################################################
+
+    public function testDoubleField()
+    {
+        $m = new TestMessage();
+
+        // Set integer.
+        $m->setOptionalDouble(1);
+        $this->assertEquals(1.0, $m->getOptionalDouble(), '', MAX_FLOAT_DIFF);
+
+        // Set float.
+        $m->setOptionalDouble(1.1);
+        $this->assertEquals(1.1, $m->getOptionalDouble(), '', MAX_FLOAT_DIFF);
+
+        // Set string.
+        $m->setOptionalDouble('2');
+        $this->assertEquals(2.0, $m->getOptionalDouble(), '', MAX_FLOAT_DIFF);
+        $m->setOptionalDouble('3.1');
+        $this->assertEquals(3.1, $m->getOptionalDouble(), '', MAX_FLOAT_DIFF);
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testDoubleFieldInvalidTypeFail()
+    {
+        $m = new TestMessage();
+        $m->setOptionalDouble(new TestMessage());
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testDoubleFieldInvalidStringFail()
+    {
+        $m = new TestMessage();
+        $m->setOptionalDouble('abc');
+    }
+
+    #########################################################
+    # Test bool field.
+    #########################################################
+
+    public function testBoolField()
+    {
+        $m = new TestMessage();
+
+        // Set bool.
+        $m->setOptionalBool(true);
+        $this->assertSame(true, $m->getOptionalBool());
+
+        // Set integer.
+        $m->setOptionalBool(-1);
+        $this->assertSame(true, $m->getOptionalBool());
+
+        // Set float.
+        $m->setOptionalBool(1.1);
+        $this->assertSame(true, $m->getOptionalBool());
+
+        // Set string.
+        $m->setOptionalBool('');
+        $this->assertSame(false, $m->getOptionalBool());
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testBoolFieldInvalidStringFail()
+    {
+        $m = new TestMessage();
+        $m->setOptionalBool(new TestMessage());
+    }
+
+    #########################################################
+    # Test string field.
+    #########################################################
+
+    public function testStringField()
+    {
+        $m = new TestMessage();
+
+        // Set string.
+        $m->setOptionalString('abc');
+        $this->assertSame('abc', $m->getOptionalString());
+
+        // Set integer.
+        $m->setOptionalString(1);
+        $this->assertSame('1', $m->getOptionalString());
+
+        // Set double.
+        $m->setOptionalString(1.1);
+        $this->assertSame('1.1', $m->getOptionalString());
+
+        // Set bool.
+        $m->setOptionalString(true);
+        $this->assertSame('1', $m->getOptionalString());
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testStringFieldInvalidUTF8Fail()
+    {
+        $m = new TestMessage();
+        $hex = hex2bin("ff");
+        $m->setOptionalString($hex);
+    }
+
+    #########################################################
+    # Test bytes field.
+    #########################################################
+
+    public function testBytesField()
+    {
+        $m = new TestMessage();
+
+        // Set string.
+        $m->setOptionalBytes('abc');
+        $this->assertSame('abc', $m->getOptionalBytes());
+
+        // Set integer.
+        $m->setOptionalBytes(1);
+        $this->assertSame('1', $m->getOptionalBytes());
+
+        // Set double.
+        $m->setOptionalBytes(1.1);
+        $this->assertSame('1.1', $m->getOptionalBytes());
+
+        // Set bool.
+        $m->setOptionalBytes(true);
+        $this->assertSame('1', $m->getOptionalBytes());
+    }
+
+      public function testBytesFieldInvalidUTF8Success()
+      {
+          $m = new TestMessage();
+          $hex = hex2bin("ff");
+          $m->setOptionalBytes($hex);
+      }
+
+    #########################################################
+    # Test message field.
+    #########################################################
+
+    public function testMessageField()
+    {
+        $m = new TestMessage();
+
+        $sub_m = new TestMessage_Sub();
+        $sub_m->setA(1);
+        $m->setOptionalMessage($sub_m);
+        $this->assertSame(1, $m->getOptionalMessage()->getA());
+
+        $null = null;
+        $m->setOptionalMessage($null);
+        $this->assertNull($m->getOptionalMessage());
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testMessageFieldWrongTypeFail()
+    {
+        $m = new TestMessage();
+        $a = 1;
+        $m->setOptionalMessage($a);
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testMessageFieldWrongClassFail()
+    {
+        $m = new TestMessage();
+        $m->setOptionalMessage(new TestMessage());
+    }
+
+    #########################################################
+    # Test repeated field.
+    #########################################################
+
+    public function testRepeatedField()
+    {
+        $m = new TestMessage();
+
+        $repeated_int32 = new RepeatedField(GPBType::INT32);
+        $m->setRepeatedInt32($repeated_int32);
+        $this->assertSame($repeated_int32, $m->getRepeatedInt32());
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testRepeatedFieldWrongTypeFail()
+    {
+        $m = new TestMessage();
+        $a = 1;
+        $m->setRepeatedInt32($a);
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testRepeatedFieldWrongObjectFail()
+    {
+        $m = new TestMessage();
+        $m->setRepeatedInt32($m);
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testRepeatedFieldWrongRepeatedTypeFail()
+    {
+        $m = new TestMessage();
+
+        $repeated_int32 = new RepeatedField(GPBType::UINT32);
+        $m->setRepeatedInt32($repeated_int32);
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testRepeatedFieldWrongRepeatedMessageClassFail()
+    {
+        $m = new TestMessage();
+
+        $repeated_message = new RepeatedField(GPBType::MESSAGE,
+                                              TestMessage::class);
+        $m->setRepeatedMessage($repeated_message);
+    }
+
+    #########################################################
+    # Test oneof field.
+    #########################################################
+
+    public function testOneofField() {
+        $m = new TestMessage();
+
+        $m->setOneofInt32(1);
+        $this->assertSame(1, $m->getOneofInt32());
+        $this->assertSame(0.0, $m->getOneofFloat());
+        $this->assertSame('', $m->getOneofString());
+        $this->assertSame(NULL, $m->getOneofMessage());
+
+        $m->setOneofFloat(2.0);
+        $this->assertSame(0, $m->getOneofInt32());
+        $this->assertSame(2.0, $m->getOneofFloat());
+        $this->assertSame('', $m->getOneofString());
+        $this->assertSame(NULL, $m->getOneofMessage());
+
+        $m->setOneofString('abc');
+        $this->assertSame(0, $m->getOneofInt32());
+        $this->assertSame(0.0, $m->getOneofFloat());
+        $this->assertSame('abc', $m->getOneofString());
+        $this->assertSame(NULL, $m->getOneofMessage());
+
+        $sub_m = new TestMessage_Sub();
+        $sub_m->setA(1);
+        $m->setOneofMessage($sub_m);
+        $this->assertSame(0, $m->getOneofInt32());
+        $this->assertSame(0.0, $m->getOneofFloat());
+        $this->assertSame('', $m->getOneofString());
+        $this->assertSame(1, $m->getOneofMessage()->getA());
+    }
+}
diff --git a/php/tests/map_field_test.php b/php/tests/map_field_test.php
new file mode 100644
index 0000000..d79d0da
--- /dev/null
+++ b/php/tests/map_field_test.php
@@ -0,0 +1,648 @@
+<?php
+
+require_once('test.pb.php');
+require_once('test_util.php');
+
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\MapField;
+use Foo\TestMessage;
+use Foo\TestMessage_Sub;
+
+class MapFieldTest extends PHPUnit_Framework_TestCase {
+
+    #########################################################
+    # Test int32 field.
+    #########################################################
+
+    public function testInt32() {
+        $arr = new MapField(GPBType::INT32, GPBType::INT32);
+
+        // Test integer argument.
+        $arr[MAX_INT32] = MAX_INT32;
+        $this->assertSame(MAX_INT32, $arr[MAX_INT32]);
+        $arr[MIN_INT32] = MIN_INT32;
+        $this->assertSame(MIN_INT32, $arr[MIN_INT32]);
+        $this->assertEquals(2, count($arr));
+        $this->assertTrue(isset($arr[MAX_INT32]));
+        $this->assertTrue(isset($arr[MIN_INT32]));
+        unset($arr[MAX_INT32]);
+        unset($arr[MIN_INT32]);
+        $this->assertEquals(0, count($arr));
+
+        // Test float argument.
+        $arr[1.9] = 1.9;
+        $arr[2.1] = 2.1;
+        $this->assertSame(1, $arr[1]);
+        $this->assertSame(2, $arr[2]);
+        $arr[MAX_INT32_FLOAT] = MAX_INT32_FLOAT;
+        $this->assertSame(MAX_INT32, $arr[MAX_INT32]);
+        $arr[MIN_INT32_FLOAT] = MIN_INT32_FLOAT;
+        $this->assertSame(MIN_INT32, $arr[MIN_INT32]);
+        $this->assertEquals(4, count($arr));
+        unset($arr[1.9]);
+        unset($arr[2.9]);
+        unset($arr[MAX_INT32_FLOAT]);
+        unset($arr[MIN_INT32_FLOAT]);
+        $this->assertEquals(0, count($arr));
+
+        // Test string argument.
+        $arr['2'] = '2';
+        $this->assertSame(2, $arr[2]);
+        $arr['3.1'] = '3.1';
+        $this->assertSame(3, $arr[3]);
+        $arr[MAX_INT32_STRING] = MAX_INT32_STRING;
+        $this->assertSame(MAX_INT32, $arr[MAX_INT32]);
+        $this->assertEquals(3, count($arr));
+        unset($arr['2']);
+        unset($arr['3.1']);
+        unset($arr[MAX_INT32_STRING]);
+        $this->assertEquals(0, count($arr));
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testInt32SetStringKeyFail()
+    {
+        $arr = new MapField(GPBType::INT32, GPBType::INT32);
+        $arr ['abc']= 0;
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testInt32SetStringValueFail()
+    {
+        $arr = new MapField(GPBType::INT32, GPBType::INT32);
+        $arr [0]= 'abc';
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testInt32SetMessageKeyFail()
+    {
+        $arr = new MapField(GPBType::INT32, GPBType::INT32);
+        $arr [new TestMessage_Sub()]= 0;
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testInt32SetMessageValueFail()
+    {
+        $arr = new MapField(GPBType::INT32, GPBType::INT32);
+        $arr [0]= new TestMessage_Sub();
+    }
+
+    #########################################################
+    # Test uint32 field.
+    #########################################################
+
+    public function testUint32() {
+        $arr = new MapField(GPBType::UINT32, GPBType::UINT32);
+
+        // Test integer argument.
+        $arr[MAX_UINT32] = MAX_UINT32;
+        $this->assertSame(-1, $arr[-1]);
+        $this->assertEquals(1, count($arr));
+        unset($arr[MAX_UINT32]);
+        $this->assertEquals(0, count($arr));
+
+        $arr[-1] = -1;
+        $this->assertSame(-1, $arr[-1]);
+        $arr[MIN_UINT32] = MIN_UINT32;
+        $this->assertSame(MIN_UINT32, $arr[MIN_UINT32]);
+        $this->assertEquals(2, count($arr));
+        unset($arr[-1]);
+        unset($arr[MIN_UINT32]);
+        $this->assertEquals(0, count($arr));
+
+        // Test float argument.
+        $arr[MAX_UINT32_FLOAT] = MAX_UINT32_FLOAT;
+        $this->assertSame(-1, $arr[-1]);
+        $this->assertEquals(1, count($arr));
+        unset($arr[MAX_UINT32_FLOAT]);
+        $this->assertEquals(0, count($arr));
+
+        $arr[3.1] = 3.1;
+        $this->assertSame(3, $arr[3]);
+        $arr[-1.0] = -1.0;
+        $this->assertSame(-1, $arr[-1]);
+        $arr[MIN_UINT32_FLOAT] = MIN_UINT32_FLOAT;
+        $this->assertSame(MIN_UINT32, $arr[MIN_UINT32]);
+        $this->assertEquals(3, count($arr));
+        unset($arr[3.1]);
+        unset($arr[-1.0]);
+        unset($arr[MIN_UINT32_FLOAT]);
+        $this->assertEquals(0, count($arr));
+
+        // Test string argument.
+        $arr[MAX_UINT32_STRING] = MAX_UINT32_STRING;
+        $this->assertSame(-1, $arr[-1]);
+        $this->assertEquals(1, count($arr));
+        unset($arr[MAX_UINT32_STRING]);
+        $this->assertEquals(0, count($arr));
+
+        $arr['7'] = '7';
+        $this->assertSame(7, $arr[7]);
+        $arr['3.1'] = '3.1';
+        $this->assertSame(3, $arr[3]);
+        $arr['-1.0'] = '-1.0';
+        $this->assertSame(-1, $arr[-1]);
+        $arr[MIN_UINT32_STRING] = MIN_UINT32_STRING;
+        $this->assertSame(MIN_UINT32, $arr[MIN_UINT32]);
+        $this->assertEquals(4, count($arr));
+        unset($arr['7']);
+        unset($arr['3.1']);
+        unset($arr['-1.0']);
+        unset($arr[MIN_UINT32_STRING]);
+        $this->assertEquals(0, count($arr));
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testUint32SetStringKeyFail()
+    {
+        $arr = new MapField(GPBType::UINT32, GPBType::UINT32);
+        $arr ['abc']= 0;
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testUint32SetStringValueFail()
+    {
+        $arr = new MapField(GPBType::UINT32, GPBType::UINT32);
+        $arr [0]= 'abc';
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testUint32SetMessageKeyFail()
+    {
+        $arr = new MapField(GPBType::UINT32, GPBType::UINT32);
+        $arr [new TestMessage_Sub()]= 0;
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testUint32SetMessageValueFail()
+    {
+        $arr = new MapField(GPBType::UINT32, GPBType::UINT32);
+        $arr [0]= new TestMessage_Sub();
+    }
+
+    #########################################################
+    # Test int64 field.
+    #########################################################
+
+    public function testInt64() {
+        $arr = new MapField(GPBType::INT64, GPBType::INT64);
+
+        // Test integer argument.
+        $arr[MAX_INT64] = MAX_INT64;
+        $this->assertSame(MAX_INT64, $arr[MAX_INT64]);
+        $arr[MIN_INT64] = MIN_INT64;
+        $this->assertEquals(MIN_INT64, $arr[MIN_INT64]);
+        $this->assertEquals(2, count($arr));
+        unset($arr[MAX_INT64]);
+        unset($arr[MIN_INT64]);
+        $this->assertEquals(0, count($arr));
+
+        // Test float argument.
+        $arr[1.1] = 1.1;
+        $this->assertSame(1, $arr[1]);
+        $this->assertEquals(1, count($arr));
+        unset($arr[1.1]);
+        $this->assertEquals(0, count($arr));
+
+        // Test string argument.
+        $arr['2'] = '2';
+        $this->assertSame(2, $arr[2]);
+        $arr['3.1'] = '3.1';
+        $this->assertSame(3, $arr[3]);
+        $arr[MAX_INT64_STRING] = MAX_INT64_STRING;
+        $this->assertSame(MAX_INT64, $arr[MAX_INT64]);
+        $arr[MIN_INT64_STRING] = MIN_INT64_STRING;
+        $this->assertEquals(MIN_INT64, $arr[MIN_INT64]);
+        $this->assertEquals(4, count($arr));
+        unset($arr['2']);
+        unset($arr['3.1']);
+        unset($arr[MAX_INT64_STRING]);
+        unset($arr[MIN_INT64_STRING]);
+        $this->assertEquals(0, count($arr));
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testInt64SetStringKeyFail()
+    {
+        $arr = new MapField(GPBType::INT64, GPBType::INT64);
+        $arr ['abc']= 0;
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testInt64SetStringValueFail()
+    {
+        $arr = new MapField(GPBType::INT64, GPBType::INT64);
+        $arr [0]= 'abc';
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testInt64SetMessageKeyFail()
+    {
+        $arr = new MapField(GPBType::INT64, GPBType::INT64);
+        $arr [new TestMessage_Sub()]= 0;
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testInt64SetMessageValueFail()
+    {
+        $arr = new MapField(GPBType::INT64, GPBType::INT64);
+        $arr [0]= new TestMessage_Sub();
+    }
+
+    #########################################################
+    # Test uint64 field.
+    #########################################################
+
+    public function testUint64() {
+        $arr = new MapField(GPBType::UINT64, GPBType::UINT64);
+
+        // Test integer argument.
+        $arr[MAX_UINT64] = MAX_UINT64;
+        $this->assertEquals(MAX_UINT64, $arr[MAX_UINT64]);
+        $this->assertEquals(1, count($arr));
+        unset($arr[MAX_UINT64]);
+        $this->assertEquals(0, count($arr));
+
+        // Test float argument.
+        $arr[1.1] = 1.1;
+        $this->assertSame(1, $arr[1]);
+        $this->assertEquals(1, count($arr));
+        unset($arr[1.1]);
+        $this->assertEquals(0, count($arr));
+
+        // Test string argument.
+        $arr['2'] = '2';
+        $this->assertSame(2, $arr[2]);
+        $arr['3.1'] = '3.1';
+        $this->assertSame(3, $arr[3]);
+        $arr[MAX_UINT64_STRING] = MAX_UINT64_STRING;
+        $this->assertEquals(MAX_UINT64, $arr[MAX_UINT64]);
+        $this->assertEquals(3, count($arr));
+        unset($arr['2']);
+        unset($arr['3.1']);
+        unset($arr[MAX_UINT64_STRING]);
+        $this->assertEquals(0, count($arr));
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testUint64SetStringKeyFail()
+    {
+        $arr = new MapField(GPBType::UINT64, GPBType::UINT64);
+        $arr ['abc']= 0;
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testUint64SetStringValueFail()
+    {
+        $arr = new MapField(GPBType::UINT64, GPBType::UINT64);
+        $arr [0]= 'abc';
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testUint64SetMessageKeyFail()
+    {
+        $arr = new MapField(GPBType::UINT64, GPBType::UINT64);
+        $arr [new TestMessage_Sub()]= 0;
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testUint64SetMessageValueFail()
+    {
+        $arr = new MapField(GPBType::UINT64, GPBType::UINT64);
+        $arr [0]= new TestMessage_Sub();
+    }
+
+    #########################################################
+    # Test float field.
+    #########################################################
+
+    public function testFloat() {
+        $arr = new MapField(GPBType::INT32, GPBType::FLOAT);
+
+        // Test set.
+        $arr[0] = 1;
+        $this->assertEquals(1.0, $arr[0], '', MAX_FLOAT_DIFF);
+
+        $arr[1] = 1.1;
+        $this->assertEquals(1.1, $arr[1], '', MAX_FLOAT_DIFF);
+
+        $arr[2] = '2';
+        $this->assertEquals(2.0, $arr[2], '', MAX_FLOAT_DIFF);
+        $arr[3] = '3.1';
+        $this->assertEquals(3.1, $arr[3], '', MAX_FLOAT_DIFF);
+
+        $this->assertEquals(4, count($arr));
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testFloatSetStringValueFail()
+    {
+        $arr = new MapField(GPBType::INT64, GPBType::FLOAT);
+        $arr [0]= 'abc';
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testFloatSetMessageValueFail()
+    {
+        $arr = new MapField(GPBType::INT64, GPBType::FLOAT);
+        $arr [0]= new TestMessage_Sub();
+    }
+
+    #########################################################
+    # Test double field.
+    #########################################################
+
+    public function testDouble() {
+        $arr = new MapField(GPBType::INT32, GPBType::DOUBLE);
+
+        // Test set.
+        $arr[0] = 1;
+        $this->assertEquals(1.0, $arr[0], '', MAX_FLOAT_DIFF);
+
+        $arr[1] = 1.1;
+        $this->assertEquals(1.1, $arr[1], '', MAX_FLOAT_DIFF);
+
+        $arr[2] = '2';
+        $this->assertEquals(2.0, $arr[2], '', MAX_FLOAT_DIFF);
+        $arr[3] = '3.1';
+        $this->assertEquals(3.1, $arr[3], '', MAX_FLOAT_DIFF);
+
+        $this->assertEquals(4, count($arr));
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testDoubleSetStringValueFail()
+    {
+        $arr = new MapField(GPBType::INT64, GPBType::DOUBLE);
+        $arr [0]= 'abc';
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testDoubleSetMessageValueFail()
+    {
+        $arr = new MapField(GPBType::INT64, GPBType::DOUBLE);
+        $arr [0]= new TestMessage_Sub();
+    }
+
+    #########################################################
+    # Test bool field.
+    #########################################################
+
+    public function testBool() {
+        $arr = new MapField(GPBType::BOOL, GPBType::BOOL);
+
+        // Test boolean.
+        $arr[True] = True;
+        $this->assertSame(True, $arr[True]);
+        $this->assertEquals(1, count($arr));
+        unset($arr[True]);
+        $this->assertEquals(0, count($arr));
+
+        $arr[False] = False;
+        $this->assertSame(False, $arr[False]);
+        $this->assertEquals(1, count($arr));
+        unset($arr[False]);
+        $this->assertEquals(0, count($arr));
+
+        // Test integer.
+        $arr[-1] = -1;
+        $this->assertSame(True, $arr[True]);
+        $this->assertEquals(1, count($arr));
+        unset($arr[-1]);
+        $this->assertEquals(0, count($arr));
+
+        $arr[0] = 0;
+        $this->assertSame(False, $arr[False]);
+        $this->assertEquals(1, count($arr));
+        unset($arr[0]);
+        $this->assertEquals(0, count($arr));
+
+        // Test float.
+        $arr[1.1] = 1.1;
+        $this->assertSame(True, $arr[True]);
+        $this->assertEquals(1, count($arr));
+        unset($arr[1.1]);
+        $this->assertEquals(0, count($arr));
+
+        $arr[0.0] = 0.0;
+        $this->assertSame(False, $arr[False]);
+        $this->assertEquals(1, count($arr));
+        unset($arr[0.0]);
+        $this->assertEquals(0, count($arr));
+
+        // Test string.
+        $arr['a'] = 'a';
+        $this->assertSame(True, $arr[True]);
+        $this->assertEquals(1, count($arr));
+        unset($arr['a']);
+        $this->assertEquals(0, count($arr));
+
+        $arr[''] = '';
+        $this->assertSame(False, $arr[False]);
+        $this->assertEquals(1, count($arr));
+        unset($arr['']);
+        $this->assertEquals(0, count($arr));
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testBoolSetMessageKeyFail()
+    {
+        $arr = new MapField(GPBType::BOOL, GPBType::BOOL);
+        $arr [new TestMessage_Sub()]= true;
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testBoolSetMessageValueFail()
+    {
+        $arr = new MapField(GPBType::BOOL, GPBType::BOOL);
+        $arr [true]= new TestMessage_Sub();
+    }
+
+    #########################################################
+    # Test string field.
+    #########################################################
+
+    public function testString() {
+        $arr = new MapField(GPBType::STRING, GPBType::STRING);
+
+        // Test set.
+        $arr['abc'] = 'abc';
+        $this->assertSame('abc', $arr['abc']);
+        $this->assertEquals(1, count($arr));
+        unset($arr['abc']);
+        $this->assertEquals(0, count($arr));
+
+        $arr[1] = 1;
+        $this->assertSame('1', $arr['1']);
+        $this->assertEquals(1, count($arr));
+        unset($arr[1]);
+        $this->assertEquals(0, count($arr));
+
+        $arr[1.1] = 1.1;
+        $this->assertSame('1.1', $arr['1.1']);
+        $this->assertEquals(1, count($arr));
+        unset($arr[1.1]);
+        $this->assertEquals(0, count($arr));
+
+        $arr[True] = True;
+        $this->assertSame('1', $arr['1']);
+        $this->assertEquals(1, count($arr));
+        unset($arr[True]);
+        $this->assertEquals(0, count($arr));
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testStringSetInvalidUTF8KeyFail()
+    {
+        $arr = new MapField(GPBType::STRING, GPBType::STRING);
+        $arr[hex2bin("ff")]= 'abc';
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testStringSetInvalidUTF8ValueFail()
+    {
+        $arr = new MapField(GPBType::STRING, GPBType::STRING);
+        $arr ['abc']= hex2bin("ff");
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testStringSetMessageKeyFail()
+    {
+        $arr = new MapField(GPBType::STRING, GPBType::STRING);
+        $arr [new TestMessage_Sub()]= 'abc';
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testStringSetMessageValueFail()
+    {
+        $arr = new MapField(GPBType::STRING, GPBType::STRING);
+        $arr ['abc']= new TestMessage_Sub();
+    }
+
+    #########################################################
+    # Test message field.
+    #########################################################
+
+    public function testMessage() {
+        $arr = new MapField(GPBType::INT32,
+            GPBType::MESSAGE, TestMessage_Sub::class);
+
+        // Test append.
+        $sub_m = new TestMessage_Sub();
+        $sub_m->setA(1);
+        $arr[0] = $sub_m;
+        $this->assertSame(1, $arr[0]->getA());
+
+        $null = NULL;
+        $arr[1] = $null;
+        $this->assertNull($arr[1]);
+
+        $this->assertEquals(2, count($arr));
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testMessageSetIntValueFail()
+    {
+       $arr =
+           new MapField(GPBType::INT32, GPBType::MESSAGE, TestMessage::class);
+       $arr[0] = 0;
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testMessageSetStringValueFail()
+    {
+       $arr =
+           new MapField(GPBType::INT32, GPBType::MESSAGE, TestMessage::class);
+       $arr[0] = 'abc';
+    }
+
+    /**
+     * @expectedException PHPUnit_Framework_Error
+     */
+    public function testMessageSetOtherMessageValueFail()
+    {
+       $arr =
+           new MapField(GPBType::INT32, GPBType::MESSAGE, TestMessage::class);
+       $arr[0] = new TestMessage_Sub();
+    }
+
+    #########################################################
+    # Test memory leak
+    #########################################################
+
+    // TODO(teboring): Add it back.
+    // public function testCycleLeak()
+    // {
+    //     $arr = new MapField(GPBType::INT32,
+    //         GPBType::MESSAGE, TestMessage::class);
+    //     $arr [0]= new TestMessage;
+    //     $arr[0]->SetMapRecursive($arr);
+
+    //     // Clean up memory before test.
+    //     gc_collect_cycles();
+    //     $start = memory_get_usage();
+    //     unset($arr);
+
+    //     // Explicitly trigger garbage collection.
+    //     gc_collect_cycles();
+
+    //     $end = memory_get_usage();
+    //     $this->assertLessThan($start, $end);
+    // }
+}
diff --git a/php/tests/memory_leak_test.php b/php/tests/memory_leak_test.php
new file mode 100644
index 0000000..ec54597
--- /dev/null
+++ b/php/tests/memory_leak_test.php
@@ -0,0 +1,73 @@
+<?php
+
+# phpunit has memory leak by itself. Thus, it cannot be used to test memory leak.
+
+require_once('test.pb.php');
+require_once('test_util.php');
+
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBType;
+use Foo\TestMessage;
+use Foo\TestMessage_Sub;
+
+$from = new TestMessage();
+TestUtil::setTestMessage($from);
+TestUtil::assertTestMessage($from);
+
+$data = $from->encode();
+
+$to = new TestMessage();
+$to->decode($data);
+
+TestUtil::assertTestMessage($to);
+
+$from->setRecursive($from);
+
+$arr = new RepeatedField(GPBType::MESSAGE, TestMessage::class);
+$arr []= new TestMessage;
+$arr[0]->SetRepeatedRecursive($arr);
+
+// Test oneof fields.
+$m = new TestMessage();
+
+$m->setOneofInt32(1);
+assert(1 === $m->getOneofInt32());
+assert(0.0 === $m->getOneofFloat());
+assert('' === $m->getOneofString());
+assert(NULL === $m->getOneofMessage());
+$data = $m->encode();
+$n = new TestMessage();
+$n->decode($data);
+assert(1 === $n->getOneofInt32());
+
+$m->setOneofFloat(2.0);
+assert(0 === $m->getOneofInt32());
+assert(2.0 === $m->getOneofFloat());
+assert('' === $m->getOneofString());
+assert(NULL === $m->getOneofMessage());
+$data = $m->encode();
+$n = new TestMessage();
+$n->decode($data);
+assert(2.0 === $n->getOneofFloat());
+
+$m->setOneofString('abc');
+assert(0 === $m->getOneofInt32());
+assert(0.0 === $m->getOneofFloat());
+assert('abc' === $m->getOneofString());
+assert(NULL === $m->getOneofMessage());
+$data = $m->encode();
+$n = new TestMessage();
+$n->decode($data);
+assert('abc' === $n->getOneofString());
+
+$sub_m = new TestMessage_Sub();
+$sub_m->setA(1);
+$m->setOneofMessage($sub_m);
+assert(0 === $m->getOneofInt32());
+assert(0.0 === $m->getOneofFloat());
+assert('' === $m->getOneofString());
+assert(1 === $m->getOneofMessage()->getA());
+$data = $m->encode();
+$n = new TestMessage();
+$n->decode($data);
+assert(1 === $n->getOneofMessage()->getA());
diff --git a/php/tests/php_implementation_test.php b/php/tests/php_implementation_test.php
new file mode 100644
index 0000000..82941dd
--- /dev/null
+++ b/php/tests/php_implementation_test.php
@@ -0,0 +1,443 @@
+<?php
+
+require_once('test.pb.php');
+require_once('test_base.php');
+require_once('test_util.php');
+
+use Foo\TestMessage;
+use Foo\TestMessage_Sub;
+use Foo\TestPackedMessage;
+use Google\Protobuf\Internal\InputStream;
+use Google\Protobuf\Internal\FileDescriptorSet;
+use Google\Protobuf\Internal\GPBUtil;
+use Google\Protobuf\Internal\Int64;
+use Google\Protobuf\Internal\Uint64;
+use Google\Protobuf\Internal\GPBLabel;
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\GPBWire;
+use Google\Protobuf\Internal\OutputStream;
+use Google\Protobuf\Internal\RepeatedField;
+
+class ImplementationTest extends TestBase
+{
+
+    public function testReadInt32()
+    {
+        $value = null;
+
+        // Positive number.
+        $input = new InputStream(hex2bin("01"));
+        GPBWire::readInt32($input, $value);
+        $this->assertSame(1, $value);
+
+        // Negative number.
+        $input = new InputStream(hex2bin("ffffffff0f"));
+        GPBWire::readInt32($input, $value);
+        $this->assertSame(-1, $value);
+
+        // Discard overflow bits.
+        $input = new InputStream(hex2bin("ffffffff7f"));
+        GPBWire::readInt32($input, $value);
+        $this->assertSame(-1, $value);
+    }
+
+    public function testReadUint32()
+    {
+        $value = null;
+
+        // Positive number.
+        $input = new InputStream(hex2bin("01"));
+        GPBWire::readUint32($input, $value);
+        $this->assertSame(1, $value);
+
+        // Max uint32.
+        $input = new InputStream(hex2bin("ffffffff0f"));
+        GPBWire::readUint32($input, $value);
+        $this->assertSame(-1, $value);
+
+        // Discard overflow bits.
+        $input = new InputStream(hex2bin("ffffffff7f"));
+        GPBWire::readUint32($input, $value);
+        $this->assertSame(-1, $value);
+    }
+
+    public function testReadInt64()
+    {
+        $value = null;
+
+        // Positive number.
+        $input = new InputStream(hex2bin("01"));
+        GPBWire::readInt64($input, $value);
+        $this->assertSame(1, $value->toInteger());
+
+        // Negative number.
+        $input = new InputStream(hex2bin("ffffffffffffffffff01"));
+        GPBWire::readInt64($input, $value);
+        $this->assertSame(-1, $value->toInteger());
+
+        // Discard overflow bits.
+        $input = new InputStream(hex2bin("ffffffffffffffffff0f"));
+        GPBWire::readInt64($input, $value);
+        $this->assertSame(-1, $value->toInteger());
+    }
+
+    public function testReadUint64()
+    {
+        $value = null;
+
+        // Positive number.
+        $input = new InputStream(hex2bin("01"));
+        GPBWire::readUint64($input, $value);
+        $this->assertSame(1, $value->toInteger());
+
+        // Negative number.
+        $input = new InputStream(hex2bin("FFFFFFFFFFFFFFFFFF01"));
+        GPBWire::readUint64($input, $value);
+        $this->assertSame(-1, $value->toInteger());
+
+        // Discard overflow bits.
+        $input = new InputStream(hex2bin("FFFFFFFFFFFFFFFFFF0F"));
+        GPBWire::readUint64($input, $value);
+        $this->assertSame(-1, $value->toInteger());
+    }
+
+    public function testReadSint32()
+    {
+        $value = null;
+
+        $input = new InputStream(hex2bin("00"));
+        GPBWire::readSint32($input, $value);
+        $this->assertSame(0, $value);
+
+        $input = new InputStream(hex2bin("01"));
+        GPBWire::readSint32($input, $value);
+        $this->assertSame(-1, $value);
+
+        $input = new InputStream(hex2bin("02"));
+        GPBWire::readSint32($input, $value);
+        $this->assertSame(1, $value);
+    }
+
+    public function testReadSint64()
+    {
+        $value = null;
+
+        $input = new InputStream(hex2bin("00"));
+        GPBWire::readSint64($input, $value);
+        $this->assertEquals(GPBUtil::Int64(0), $value);
+
+        $input = new InputStream(hex2bin("01"));
+        GPBWire::readSint64($input, $value);
+        $this->assertEquals(GPBUtil::Int64(-1), $value);
+
+        $input = new InputStream(hex2bin("02"));
+        GPBWire::readSint64($input, $value);
+        $this->assertEquals(GPBUtil::Int64(1), $value);
+    }
+
+    public function testReadFixed32()
+    {
+        $value = null;
+        $input = new InputStream(hex2bin("12345678"));
+        GPBWire::readFixed32($input, $value);
+        $this->assertSame(0x78563412, $value);
+    }
+
+    public function testReadFixed64()
+    {
+        $value = null;
+        $input = new InputStream(hex2bin("1234567812345678"));
+        GPBWire::readFixed64($input, $value);
+        $this->assertEquals(Uint64::newValue(0x78563412, 0x78563412), $value);
+    }
+
+    public function testReadSfixed32()
+    {
+        $value = null;
+        $input = new InputStream(hex2bin("12345678"));
+        GPBWire::readSfixed32($input, $value);
+        $this->assertSame(0x78563412, $value);
+    }
+
+    public function testReadFloat()
+    {
+        $value = null;
+        $input = new InputStream(hex2bin("0000803F"));
+        GPBWire::readFloat($input, $value);
+        $this->assertSame(1.0, $value);
+    }
+
+    public function testReadBool()
+    {
+        $value = null;
+
+        $input = new InputStream(hex2bin("00"));
+        GPBWire::readBool($input, $value);
+        $this->assertSame(false, $value);
+
+        $input = new InputStream(hex2bin("01"));
+        GPBWire::readBool($input, $value);
+        $this->assertSame(true, $value);
+    }
+
+    public function testReadDouble()
+    {
+        $value = null;
+        $input = new InputStream(hex2bin("000000000000F03F"));
+        GPBWire::readDouble($input, $value);
+        $this->assertSame(1.0, $value);
+    }
+
+    public function testReadSfixed64()
+    {
+        $value = null;
+        $input = new InputStream(hex2bin("1234567812345678"));
+        GPBWire::readSfixed64($input, $value);
+        $this->assertEquals(Int64::newValue(0x78563412, 0x78563412), $value);
+    }
+
+    public function testZigZagEncodeDecode()
+    {
+        $this->assertSame(0, GPBWire::zigZagEncode32(0));
+        $this->assertSame(1, GPBWire::zigZagEncode32(-1));
+        $this->assertSame(2, GPBWire::zigZagEncode32(1));
+        $this->assertSame(3, GPBWire::zigZagEncode32(-2));
+        $this->assertSame(0x7FFFFFFE, GPBWire::zigZagEncode32(0x3FFFFFFF));
+        $this->assertSame(0x7FFFFFFF, GPBWire::zigZagEncode32(0xC0000000));
+        $this->assertSame(-2, GPBWire::zigZagEncode32(0x7FFFFFFF));
+        $this->assertSame(-1, GPBWire::zigZagEncode32(0x80000000));
+
+        $this->assertSame(0,  GPBWire::zigZagDecode32(0));
+        $this->assertSame(-1, GPBWire::zigZagDecode32(1));
+        $this->assertSame(1,  GPBWire::zigZagDecode32(2));
+        $this->assertSame(-2, GPBWire::zigZagDecode32(3));
+        $this->assertSame(0x3FFFFFFF,  GPBWire::zigZagDecode32(0x7FFFFFFE));
+        $this->assertSame(-1073741824, GPBWire::zigZagDecode32(0x7FFFFFFF));
+        $this->assertSame(0x7FFFFFFF,  GPBWire::zigZagDecode32(0xFFFFFFFE));
+        $this->assertSame(-2147483648, GPBWire::zigZagDecode32(0xFFFFFFFF));
+
+        $this->assertEquals(GPBUtil::Uint64(0),
+                        GPBWire::zigZagEncode64(GPBUtil::Int64(0)));
+        $this->assertEquals(GPBUtil::Uint64(1),
+                        GPBWire::zigZagEncode64(GPBUtil::Int64(-1)));
+        $this->assertEquals(GPBUtil::Uint64(2),
+                        GPBWire::zigZagEncode64(GPBUtil::Int64(1)));
+        $this->assertEquals(GPBUtil::Uint64(3),
+                        GPBWire::zigZagEncode64(GPBUtil::Int64(-2)));
+        $this->assertEquals(
+        GPBUtil::Uint64(0x000000007FFFFFFE),
+        GPBWire::zigZagEncode64(GPBUtil::Int64(0x000000003FFFFFFF)));
+        $this->assertEquals(
+        GPBUtil::Uint64(0x000000007FFFFFFF),
+        GPBWire::zigZagEncode64(GPBUtil::Int64(0xFFFFFFFFC0000000)));
+        $this->assertEquals(
+        GPBUtil::Uint64(0x00000000FFFFFFFE),
+        GPBWire::zigZagEncode64(GPBUtil::Int64(0x000000007FFFFFFF)));
+        $this->assertEquals(
+        GPBUtil::Uint64(0x00000000FFFFFFFF),
+        GPBWire::zigZagEncode64(GPBUtil::Int64(0xFFFFFFFF80000000)));
+        $this->assertEquals(
+        Uint64::newValue(4294967295, 4294967294),
+        GPBWire::zigZagEncode64(GPBUtil::Int64(0x7FFFFFFFFFFFFFFF)));
+        $this->assertEquals(
+        Uint64::newValue(4294967295, 4294967295),
+        GPBWire::zigZagEncode64(GPBUtil::Int64(0x8000000000000000)));
+
+        $this->assertEquals(GPBUtil::Int64(0),
+                        GPBWire::zigZagDecode64(GPBUtil::Uint64(0)));
+        $this->assertEquals(GPBUtil::Int64(-1),
+                        GPBWire::zigZagDecode64(GPBUtil::Uint64(1)));
+        $this->assertEquals(GPBUtil::Int64(1),
+                        GPBWire::zigZagDecode64(GPBUtil::Uint64(2)));
+        $this->assertEquals(GPBUtil::Int64(-2),
+                        GPBWire::zigZagDecode64(GPBUtil::Uint64(3)));
+
+        // Round trip
+        $this->assertSame(0, GPBWire::zigZagDecode32(GPBWire::zigZagEncode32(0)));
+        $this->assertSame(1, GPBWire::zigZagDecode32(GPBWire::zigZagEncode32(1)));
+        $this->assertSame(-1, GPBWire::zigZagDecode32(GPBWire::zigZagEncode32(-1)));
+        $this->assertSame(14927,
+                      GPBWire::zigZagDecode32(GPBWire::zigZagEncode32(14927)));
+        $this->assertSame(-3612,
+                      GPBWire::zigZagDecode32(GPBWire::zigZagEncode32(-3612)));
+    }
+
+    public function testDecode()
+    {
+        $m = new TestMessage();
+        $m->decode(TestUtil::getGoldenTestMessage());
+        TestUtil::assertTestMessage($m);
+    }
+
+    public function testDescriptorDecode()
+    {
+        $file_desc_set = new FileDescriptorSet();
+        $file_desc_set->decode(hex2bin(
+            "0a3b0a12746573745f696e636c7564652e70726f746f120362617222180a" .
+            "0b54657374496e636c75646512090a0161180120012805620670726f746f33"));
+
+        $this->assertSame(1, sizeof($file_desc_set->getFile()));
+
+        $file_desc = $file_desc_set->getFile()[0];
+        $this->assertSame("test_include.proto", $file_desc->getName());
+        $this->assertSame("bar", $file_desc->getPackage());
+        $this->assertSame(0, sizeof($file_desc->getDependency()));
+        $this->assertSame(1, sizeof($file_desc->getMessageType()));
+        $this->assertSame(0, sizeof($file_desc->getEnumType()));
+        $this->assertSame("proto3", $file_desc->getSyntax());
+
+        $desc = $file_desc->getMessageType()[0];
+        $this->assertSame("TestInclude", $desc->getName());
+        $this->assertSame(1, sizeof($desc->getField()));
+        $this->assertSame(0, sizeof($desc->getNestedType()));
+        $this->assertSame(0, sizeof($desc->getEnumType()));
+        $this->assertSame(0, sizeof($desc->getOneofDecl()));
+
+        $field = $desc->getField()[0];
+        $this->assertSame("a", $field->getName());
+        $this->assertSame(1, $field->getNumber());
+        $this->assertSame(GPBLabel::OPTIONAL, $field->getLabel());
+        $this->assertSame(GPBType::INT32, $field->getType());
+    }
+
+    public function testReadVarint64()
+    {
+        $var = 0;
+
+        // Empty buffer.
+        $input = new InputStream(hex2bin(''));
+        $this->assertFalse($input->readVarint64($var));
+
+        // The largest varint is 10 bytes long.
+        $input = new InputStream(hex2bin('8080808080808080808001'));
+        $this->assertFalse($input->readVarint64($var));
+
+        // Corrupted varint.
+        $input = new InputStream(hex2bin('808080'));
+        $this->assertFalse($input->readVarint64($var));
+
+        // Normal case.
+        $input = new InputStream(hex2bin('808001'));
+        $this->assertTrue($input->readVarint64($var));
+        $this->assertSame(16384, $var->toInteger());
+        $this->assertFalse($input->readVarint64($var));
+
+        // Read two varint.
+        $input = new InputStream(hex2bin('808001808002'));
+        $this->assertTrue($input->readVarint64($var));
+        $this->assertSame(16384, $var->toInteger());
+        $this->assertTrue($input->readVarint64($var));
+        $this->assertSame(32768, $var->toInteger());
+        $this->assertFalse($input->readVarint64($var));
+    }
+
+    public function testReadVarint32()
+    {
+        $var = 0;
+
+        // Empty buffer.
+        $input = new InputStream(hex2bin(''));
+        $this->assertFalse($input->readVarint32($var));
+
+        // The largest varint is 10 bytes long.
+        $input = new InputStream(hex2bin('8080808080808080808001'));
+        $this->assertFalse($input->readVarint32($var));
+
+        // Corrupted varint.
+        $input = new InputStream(hex2bin('808080'));
+        $this->assertFalse($input->readVarint32($var));
+
+        // Normal case.
+        $input = new InputStream(hex2bin('808001'));
+        $this->assertTrue($input->readVarint32($var));
+        $this->assertSame(16384, $var);
+        $this->assertFalse($input->readVarint32($var));
+
+        // Read two varint.
+        $input = new InputStream(hex2bin('808001808002'));
+        $this->assertTrue($input->readVarint32($var));
+        $this->assertSame(16384, $var);
+        $this->assertTrue($input->readVarint32($var));
+        $this->assertSame(32768, $var);
+        $this->assertFalse($input->readVarint32($var));
+
+        // Read a 64-bit integer. High-order bits should be discarded.
+        $input = new InputStream(hex2bin('808081808001'));
+        $this->assertTrue($input->readVarint32($var));
+        $this->assertSame(16384, $var);
+        $this->assertFalse($input->readVarint32($var));
+    }
+
+    public function testReadTag()
+    {
+        $input = new InputStream(hex2bin('808001'));
+        $tag = $input->readTag();
+        $this->assertSame(16384, $tag);
+        $tag = $input->readTag();
+        $this->assertSame(0, $tag);
+    }
+
+    public function testPushPopLimit()
+    {
+        $input = new InputStream(hex2bin('808001'));
+        $old_limit = $input->pushLimit(0);
+        $tag = $input->readTag();
+        $this->assertSame(0, $tag);
+        $input->popLimit($old_limit);
+        $tag = $input->readTag();
+        $this->assertSame(16384, $tag);
+    }
+
+    public function testReadRaw()
+    {
+        $input = new InputStream(hex2bin('808001'));
+        $buffer = null;
+
+        $this->assertTrue($input->readRaw(3, $buffer));
+        $this->assertSame(hex2bin('808001'), $buffer);
+
+        $this->assertFalse($input->readRaw(1, $buffer));
+    }
+
+    public function testWriteVarint32()
+    {
+        $output = new OutputStream(3);
+        $output->writeVarint32(16384);
+        $this->assertSame(hex2bin('808001'), $output->getData());
+    }
+
+    public function testWriteVarint64()
+    {
+        $output = new OutputStream(10);
+        $output->writeVarint64(-43);
+        $this->assertSame(hex2bin('D5FFFFFFFFFFFFFFFF01'), $output->getData());
+    }
+
+    public function testWriteLittleEndian32()
+    {
+        $output = new OutputStream(4);
+        $output->writeLittleEndian32(46);
+        $this->assertSame(hex2bin('2E000000'), $output->getData());
+    }
+
+    public function testWriteLittleEndian64()
+    {
+        $output = new OutputStream(8);
+        $output->writeLittleEndian64(47);
+        $this->assertSame(hex2bin('2F00000000000000'), $output->getData());
+    }
+
+    public function testByteSize()
+    {
+        $m = new TestMessage();
+        TestUtil::setTestMessage($m);
+        $this->assertSame(447, $m->byteSize());
+    }
+
+    public function testPackedByteSize()
+    {
+        $m = new TestPackedMessage();
+        TestUtil::setTestPackedMessage($m);
+        $this->assertSame(156, $m->byteSize());
+    }
+}
diff --git a/php/tests/test.pb.php b/php/tests/test.pb.php
new file mode 100644
index 0000000..ed316b6
--- /dev/null
+++ b/php/tests/test.pb.php
@@ -0,0 +1,1385 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: test.proto
+
+namespace Foo;
+
+require_once('test_include.pb.php');
+use Google\Protobuf\Internal\DescriptorPool;
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+class TestMessage extends \Google\Protobuf\Internal\Message
+{
+    private $optional_int32 = 0;
+    private $optional_int64 = 0;
+    private $optional_uint32 = 0;
+    private $optional_uint64 = 0;
+    private $optional_sint32 = 0;
+    private $optional_sint64 = 0;
+    private $optional_fixed32 = 0;
+    private $optional_fixed64 = 0;
+    private $optional_sfixed32 = 0;
+    private $optional_sfixed64 = 0;
+    private $optional_float = 0.0;
+    private $optional_double = 0.0;
+    private $optional_bool = false;
+    private $optional_string = '';
+    private $optional_bytes = '';
+    private $optional_enum = 0;
+    private $optional_message = null;
+    private $optional_included_message = null;
+    private $recursive = null;
+    private $repeated_int32;
+    private $repeated_int64;
+    private $repeated_uint32;
+    private $repeated_uint64;
+    private $repeated_sint32;
+    private $repeated_sint64;
+    private $repeated_fixed32;
+    private $repeated_fixed64;
+    private $repeated_sfixed32;
+    private $repeated_sfixed64;
+    private $repeated_float;
+    private $repeated_double;
+    private $repeated_bool;
+    private $repeated_string;
+    private $repeated_bytes;
+    private $repeated_enum;
+    private $repeated_message;
+    private $repeated_recursive;
+    private $map_int32_int32;
+    private $map_int64_int64;
+    private $map_uint32_uint32;
+    private $map_uint64_uint64;
+    private $map_sint32_sint32;
+    private $map_sint64_sint64;
+    private $map_fixed32_fixed32;
+    private $map_fixed64_fixed64;
+    private $map_sfixed32_sfixed32;
+    private $map_sfixed64_sfixed64;
+    private $map_int32_float;
+    private $map_int32_double;
+    private $map_bool_bool;
+    private $map_string_string;
+    private $map_int32_bytes;
+    private $map_int32_enum;
+    private $map_int32_message;
+    private $map_recursive;
+    protected $my_oneof;
+
+    public function getOptionalInt32()
+    {
+        return $this->optional_int32;
+    }
+
+    public function setOptionalInt32($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->optional_int32 = $var;
+    }
+
+    public function getOptionalInt64()
+    {
+        return $this->optional_int64;
+    }
+
+    public function setOptionalInt64($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->optional_int64 = $var;
+    }
+
+    public function getOptionalUint32()
+    {
+        return $this->optional_uint32;
+    }
+
+    public function setOptionalUint32($var)
+    {
+        GPBUtil::checkUint32($var);
+        $this->optional_uint32 = $var;
+    }
+
+    public function getOptionalUint64()
+    {
+        return $this->optional_uint64;
+    }
+
+    public function setOptionalUint64($var)
+    {
+        GPBUtil::checkUint64($var);
+        $this->optional_uint64 = $var;
+    }
+
+    public function getOptionalSint32()
+    {
+        return $this->optional_sint32;
+    }
+
+    public function setOptionalSint32($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->optional_sint32 = $var;
+    }
+
+    public function getOptionalSint64()
+    {
+        return $this->optional_sint64;
+    }
+
+    public function setOptionalSint64($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->optional_sint64 = $var;
+    }
+
+    public function getOptionalFixed32()
+    {
+        return $this->optional_fixed32;
+    }
+
+    public function setOptionalFixed32($var)
+    {
+        GPBUtil::checkUint32($var);
+        $this->optional_fixed32 = $var;
+    }
+
+    public function getOptionalFixed64()
+    {
+        return $this->optional_fixed64;
+    }
+
+    public function setOptionalFixed64($var)
+    {
+        GPBUtil::checkUint64($var);
+        $this->optional_fixed64 = $var;
+    }
+
+    public function getOptionalSfixed32()
+    {
+        return $this->optional_sfixed32;
+    }
+
+    public function setOptionalSfixed32($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->optional_sfixed32 = $var;
+    }
+
+    public function getOptionalSfixed64()
+    {
+        return $this->optional_sfixed64;
+    }
+
+    public function setOptionalSfixed64($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->optional_sfixed64 = $var;
+    }
+
+    public function getOptionalFloat()
+    {
+        return $this->optional_float;
+    }
+
+    public function setOptionalFloat($var)
+    {
+        GPBUtil::checkFloat($var);
+        $this->optional_float = $var;
+    }
+
+    public function getOptionalDouble()
+    {
+        return $this->optional_double;
+    }
+
+    public function setOptionalDouble($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->optional_double = $var;
+    }
+
+    public function getOptionalBool()
+    {
+        return $this->optional_bool;
+    }
+
+    public function setOptionalBool($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->optional_bool = $var;
+    }
+
+    public function getOptionalString()
+    {
+        return $this->optional_string;
+    }
+
+    public function setOptionalString($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->optional_string = $var;
+    }
+
+    public function getOptionalBytes()
+    {
+        return $this->optional_bytes;
+    }
+
+    public function setOptionalBytes($var)
+    {
+        GPBUtil::checkString($var, False);
+        $this->optional_bytes = $var;
+    }
+
+    public function getOptionalEnum()
+    {
+        return $this->optional_enum;
+    }
+
+    public function setOptionalEnum($var)
+    {
+        GPBUtil::checkEnum($var, \Foo\TestEnum::class);
+        $this->optional_enum = $var;
+    }
+
+    public function getOptionalMessage()
+    {
+        return $this->optional_message;
+    }
+
+    public function setOptionalMessage(&$var)
+    {
+        GPBUtil::checkMessage($var, \Foo\TestMessage_Sub::class);
+        $this->optional_message = $var;
+    }
+
+    public function getOptionalIncludedMessage()
+    {
+        return $this->optional_included_message;
+    }
+
+    public function setOptionalIncludedMessage(&$var)
+    {
+        GPBUtil::checkMessage($var, \Bar\TestInclude::class);
+        $this->optional_included_message = $var;
+    }
+
+    public function getRecursive()
+    {
+        return $this->recursive;
+    }
+
+    public function setRecursive(&$var)
+    {
+        GPBUtil::checkMessage($var, \Foo\TestMessage::class);
+        $this->recursive = $var;
+    }
+
+    public function getRepeatedInt32()
+    {
+        return $this->repeated_int32;
+    }
+
+    public function setRepeatedInt32(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::INT32);
+        $this->repeated_int32 = $var;
+    }
+
+    public function getRepeatedInt64()
+    {
+        return $this->repeated_int64;
+    }
+
+    public function setRepeatedInt64(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::INT64);
+        $this->repeated_int64 = $var;
+    }
+
+    public function getRepeatedUint32()
+    {
+        return $this->repeated_uint32;
+    }
+
+    public function setRepeatedUint32(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::UINT32);
+        $this->repeated_uint32 = $var;
+    }
+
+    public function getRepeatedUint64()
+    {
+        return $this->repeated_uint64;
+    }
+
+    public function setRepeatedUint64(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::UINT64);
+        $this->repeated_uint64 = $var;
+    }
+
+    public function getRepeatedSint32()
+    {
+        return $this->repeated_sint32;
+    }
+
+    public function setRepeatedSint32(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::SINT32);
+        $this->repeated_sint32 = $var;
+    }
+
+    public function getRepeatedSint64()
+    {
+        return $this->repeated_sint64;
+    }
+
+    public function setRepeatedSint64(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::SINT64);
+        $this->repeated_sint64 = $var;
+    }
+
+    public function getRepeatedFixed32()
+    {
+        return $this->repeated_fixed32;
+    }
+
+    public function setRepeatedFixed32(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::FIXED32);
+        $this->repeated_fixed32 = $var;
+    }
+
+    public function getRepeatedFixed64()
+    {
+        return $this->repeated_fixed64;
+    }
+
+    public function setRepeatedFixed64(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::FIXED64);
+        $this->repeated_fixed64 = $var;
+    }
+
+    public function getRepeatedSfixed32()
+    {
+        return $this->repeated_sfixed32;
+    }
+
+    public function setRepeatedSfixed32(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::SFIXED32);
+        $this->repeated_sfixed32 = $var;
+    }
+
+    public function getRepeatedSfixed64()
+    {
+        return $this->repeated_sfixed64;
+    }
+
+    public function setRepeatedSfixed64(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::SFIXED64);
+        $this->repeated_sfixed64 = $var;
+    }
+
+    public function getRepeatedFloat()
+    {
+        return $this->repeated_float;
+    }
+
+    public function setRepeatedFloat(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::FLOAT);
+        $this->repeated_float = $var;
+    }
+
+    public function getRepeatedDouble()
+    {
+        return $this->repeated_double;
+    }
+
+    public function setRepeatedDouble(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::DOUBLE);
+        $this->repeated_double = $var;
+    }
+
+    public function getRepeatedBool()
+    {
+        return $this->repeated_bool;
+    }
+
+    public function setRepeatedBool(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::BOOL);
+        $this->repeated_bool = $var;
+    }
+
+    public function getRepeatedString()
+    {
+        return $this->repeated_string;
+    }
+
+    public function setRepeatedString(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::STRING);
+        $this->repeated_string = $var;
+    }
+
+    public function getRepeatedBytes()
+    {
+        return $this->repeated_bytes;
+    }
+
+    public function setRepeatedBytes(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::BYTES);
+        $this->repeated_bytes = $var;
+    }
+
+    public function getRepeatedEnum()
+    {
+        return $this->repeated_enum;
+    }
+
+    public function setRepeatedEnum(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::ENUM, Foo\TestEnum::class);
+        $this->repeated_enum = $var;
+    }
+
+    public function getRepeatedMessage()
+    {
+        return $this->repeated_message;
+    }
+
+    public function setRepeatedMessage(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::MESSAGE, \Foo\TestMessage_Sub::class);
+        $this->repeated_message = $var;
+    }
+
+    public function getRepeatedRecursive()
+    {
+        return $this->repeated_recursive;
+    }
+
+    public function setRepeatedRecursive(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::MESSAGE, \Foo\TestMessage::class);
+        $this->repeated_recursive = $var;
+    }
+
+    public function getOneofInt32()
+    {
+        return $this->readOneof(51);
+    }
+
+    public function setOneofInt32($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->writeOneof(51, $var);
+    }
+
+    public function getOneofInt64()
+    {
+        return $this->readOneof(52);
+    }
+
+    public function setOneofInt64($var)
+    {
+        GPBUtil::checkInt64($var);
+        $this->writeOneof(52, $var);
+    }
+
+    public function getOneofUint32()
+    {
+        return $this->readOneof(53);
+    }
+
+    public function setOneofUint32($var)
+    {
+        GPBUtil::checkUint32($var);
+        $this->writeOneof(53, $var);
+    }
+
+    public function getOneofUint64()
+    {
+        return $this->readOneof(54);
+    }
+
+    public function setOneofUint64($var)
+    {
+        GPBUtil::checkUint64($var);
+        $this->writeOneof(54, $var);
+    }
+
+    public function getOneofSint32()
+    {
+        return $this->readOneof(55);
+    }
+
+    public function setOneofSint32($var)
+    {
+        GPBUtil::checkUint32($var);
+        $this->writeOneof(55, $var);
+    }
+
+    public function getOneofSint64()
+    {
+        return $this->readOneof(56);
+    }
+
+    public function setOneofSint64($var)
+    {
+        GPBUtil::checkUint64($var);
+        $this->writeOneof(56, $var);
+    }
+
+    public function getOneofFixed32()
+    {
+        return $this->readOneof(57);
+    }
+
+    public function setOneofFixed32($var)
+    {
+        GPBUtil::checkUint32($var);
+        $this->writeOneof(57, $var);
+    }
+
+    public function getOneofFixed64()
+    {
+        return $this->readOneof(58);
+    }
+
+    public function setOneofFixed64($var)
+    {
+        GPBUtil::checkUint64($var);
+        $this->writeOneof(58, $var);
+    }
+
+    public function getOneofSfixed32()
+    {
+        return $this->readOneof(59);
+    }
+
+    public function setOneofSfixed32($var)
+    {
+        GPBUtil::checkUint32($var);
+        $this->writeOneof(59, $var);
+    }
+
+    public function getOneofSfixed64()
+    {
+        return $this->readOneof(60);
+    }
+
+    public function setOneofSfixed64($var)
+    {
+        GPBUtil::checkUint64($var);
+        $this->writeOneof(60, $var);
+    }
+
+    public function getOneofDouble()
+    {
+        return $this->readOneof(61);
+    }
+
+    public function setOneofDouble($var)
+    {
+        GPBUtil::checkDouble($var);
+        $this->writeOneof(61, $var);
+    }
+
+    public function getOneofFloat()
+    {
+        return $this->readOneof(62);
+    }
+
+    public function setOneofFloat($var)
+    {
+        GPBUtil::checkFloat($var);
+        $this->writeOneof(62, $var);
+    }
+
+    public function getOneofBool()
+    {
+        return $this->readOneof(63);
+    }
+
+    public function setOneofBool($var)
+    {
+        GPBUtil::checkBool($var);
+        $this->writeOneof(63, $var);
+    }
+
+    public function getOneofString()
+    {
+        return $this->readOneof(64);
+    }
+
+    public function setOneofString($var)
+    {
+        GPBUtil::checkString($var, True);
+        $this->writeOneof(64, $var);
+    }
+
+    public function getOneofBytes()
+    {
+        return $this->readOneof(65);
+    }
+
+    public function setOneofBytes($var)
+    {
+        GPBUtil::checkString($var, False);
+        $this->writeOneof(65, $var);
+    }
+
+    public function getOneofEnum()
+    {
+        return $this->readOneof(66);
+    }
+
+    public function setOneofEnum($var)
+    {
+        GPBUtil::checkEnum($var, \Foo\TestEnum::class);
+        $this->writeOneof(66, $var);
+    }
+
+    public function getOneofMessage()
+    {
+        return $this->readOneof(67);
+    }
+
+    public function setOneofMessage(&$var)
+    {
+        GPBUtil::checkMessage($var, \Foo\TestMessage_Sub::class);
+        $this->writeOneof(67, $var);
+    }
+
+    public function getMapInt32Int32()
+    {
+        return $this->map_int32_int32;
+    }
+
+    public function setMapInt32Int32(&$var)
+    {
+        $this->map_int32_int32 = $var;
+    }
+
+    public function getMapInt64Int64()
+    {
+        return $this->map_int64_int64;
+    }
+
+    public function setMapInt64Int64(&$var)
+    {
+        $this->map_int64_int64 = $var;
+    }
+
+    public function getMapUint32Uint32()
+    {
+        return $this->map_uint32_uint32;
+    }
+
+    public function setMapUint32Uint32(&$var)
+    {
+        $this->map_uint32_uint32 = $var;
+    }
+
+    public function getMapUint64Uint64()
+    {
+        return $this->map_uint64_uint64;
+    }
+
+    public function setMapUint64Uint64(&$var)
+    {
+        $this->map_uint64_uint64 = $var;
+    }
+
+    public function getMapSint32Sint32()
+    {
+        return $this->map_sint32_sint32;
+    }
+
+    public function setMapSint32Sint32(&$var)
+    {
+        $this->map_sint32_sint32 = $var;
+    }
+
+    public function getMapSint64Sint64()
+    {
+        return $this->map_sint64_sint64;
+    }
+
+    public function setMapSint64Sint64(&$var)
+    {
+        $this->map_sint64_sint64 = $var;
+    }
+
+    public function getMapFixed32Fixed32()
+    {
+        return $this->map_fixed32_fixed32;
+    }
+
+    public function setMapFixed32Fixed32(&$var)
+    {
+        $this->map_fixed32_fixed32 = $var;
+    }
+
+    public function getMapFixed64Fixed64()
+    {
+        return $this->map_fixed64_fixed64;
+    }
+
+    public function setMapFixed64Fixed64(&$var)
+    {
+        $this->map_fixed64_fixed64 = $var;
+    }
+
+    public function getMapSfixed32Sfixed32()
+    {
+        return $this->map_sfixed32_sfixed32;
+    }
+
+    public function setMapSfixed32Sfixed32(&$var)
+    {
+        $this->map_sfixed32_sfixed32 = $var;
+    }
+
+    public function getMapSfixed64Sfixed64()
+    {
+        return $this->map_sfixed64_sfixed64;
+    }
+
+    public function setMapSfixed64Sfixed64(&$var)
+    {
+        $this->map_sfixed64_sfixed64 = $var;
+    }
+
+    public function getMapInt32Float()
+    {
+        return $this->map_int32_float;
+    }
+
+    public function setMapInt32Float(&$var)
+    {
+        $this->map_int32_float = $var;
+    }
+
+    public function getMapInt32Double()
+    {
+        return $this->map_int32_double;
+    }
+
+    public function setMapInt32Double(&$var)
+    {
+        $this->map_int32_double = $var;
+    }
+
+    public function getMapBoolBool()
+    {
+        return $this->map_bool_bool;
+    }
+
+    public function setMapBoolBool(&$var)
+    {
+        $this->map_bool_bool = $var;
+    }
+
+    public function getMapStringString()
+    {
+        return $this->map_string_string;
+    }
+
+    public function setMapStringString(&$var)
+    {
+        $this->map_string_string = $var;
+    }
+
+    public function getMapInt32Bytes()
+    {
+        return $this->map_int32_bytes;
+    }
+
+    public function setMapInt32Bytes(&$var)
+    {
+        $this->map_int32_bytes = $var;
+    }
+
+    public function getMapInt32Enum()
+    {
+        return $this->map_int32_enum;
+    }
+
+    public function setMapInt32Enum(&$var)
+    {
+        $this->map_int32_enum = $var;
+    }
+
+    public function getMapInt32Message()
+    {
+        return $this->map_int32_message;
+    }
+
+    public function setMapInt32Message(&$var)
+    {
+        $this->map_int32_message = $var;
+    }
+
+    public function getMapRecursive()
+    {
+        return $this->map_recursive;
+    }
+
+    public function setMapRecursive(&$var)
+    {
+        $this->map_recursive = $var;
+    }
+
+    public function getMyOneof()
+    {
+        return $this->my_oneof;
+    }
+
+}
+
+class TestMessage_Sub extends \Google\Protobuf\Internal\Message
+{
+    private $a = 0;
+
+    public function getA()
+    {
+        return $this->a;
+    }
+
+    public function setA($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->a = $var;
+    }
+
+}
+
+class TestPackedMessage extends \Google\Protobuf\Internal\Message
+{
+    private $repeated_int32;
+    private $repeated_int64;
+    private $repeated_uint32;
+    private $repeated_uint64;
+    private $repeated_sint32;
+    private $repeated_sint64;
+    private $repeated_fixed32;
+    private $repeated_fixed64;
+    private $repeated_sfixed32;
+    private $repeated_sfixed64;
+    private $repeated_float;
+    private $repeated_double;
+    private $repeated_bool;
+    private $repeated_enum;
+
+    public function getRepeatedInt32()
+    {
+        return $this->repeated_int32;
+    }
+
+    public function setRepeatedInt32(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::INT32);
+        $this->repeated_int32 = $var;
+    }
+
+    public function getRepeatedInt64()
+    {
+        return $this->repeated_int64;
+    }
+
+    public function setRepeatedInt64(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::INT64);
+        $this->repeated_int64 = $var;
+    }
+
+    public function getRepeatedUint32()
+    {
+        return $this->repeated_uint32;
+    }
+
+    public function setRepeatedUint32(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::UINT32);
+        $this->repeated_uint32 = $var;
+    }
+
+    public function getRepeatedUint64()
+    {
+        return $this->repeated_uint64;
+    }
+
+    public function setRepeatedUint64(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::UINT64);
+        $this->repeated_uint64 = $var;
+    }
+
+    public function getRepeatedSint32()
+    {
+        return $this->repeated_sint32;
+    }
+
+    public function setRepeatedSint32(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::SINT32);
+        $this->repeated_sint32 = $var;
+    }
+
+    public function getRepeatedSint64()
+    {
+        return $this->repeated_sint64;
+    }
+
+    public function setRepeatedSint64(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::SINT64);
+        $this->repeated_sint64 = $var;
+    }
+
+    public function getRepeatedFixed32()
+    {
+        return $this->repeated_fixed32;
+    }
+
+    public function setRepeatedFixed32(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::FIXED32);
+        $this->repeated_fixed32 = $var;
+    }
+
+    public function getRepeatedFixed64()
+    {
+        return $this->repeated_fixed64;
+    }
+
+    public function setRepeatedFixed64(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::FIXED64);
+        $this->repeated_fixed64 = $var;
+    }
+
+    public function getRepeatedSfixed32()
+    {
+        return $this->repeated_sfixed32;
+    }
+
+    public function setRepeatedSfixed32(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::SFIXED32);
+        $this->repeated_sfixed32 = $var;
+    }
+
+    public function getRepeatedSfixed64()
+    {
+        return $this->repeated_sfixed64;
+    }
+
+    public function setRepeatedSfixed64(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::SFIXED64);
+        $this->repeated_sfixed64 = $var;
+    }
+
+    public function getRepeatedFloat()
+    {
+        return $this->repeated_float;
+    }
+
+    public function setRepeatedFloat(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::FLOAT);
+        $this->repeated_float = $var;
+    }
+
+    public function getRepeatedDouble()
+    {
+        return $this->repeated_double;
+    }
+
+    public function setRepeatedDouble(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::DOUBLE);
+        $this->repeated_double = $var;
+    }
+
+    public function getRepeatedBool()
+    {
+        return $this->repeated_bool;
+    }
+
+    public function setRepeatedBool(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::BOOL);
+        $this->repeated_bool = $var;
+    }
+
+    public function getRepeatedEnum()
+    {
+        return $this->repeated_enum;
+    }
+
+    public function setRepeatedEnum(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::ENUM, Foo\TestEnum::class);
+        $this->repeated_enum = $var;
+    }
+
+}
+
+class TestUnpackedMessage extends \Google\Protobuf\Internal\Message
+{
+    private $repeated_int32;
+    private $repeated_int64;
+    private $repeated_uint32;
+    private $repeated_uint64;
+    private $repeated_sint32;
+    private $repeated_sint64;
+    private $repeated_fixed32;
+    private $repeated_fixed64;
+    private $repeated_sfixed32;
+    private $repeated_sfixed64;
+    private $repeated_float;
+    private $repeated_double;
+    private $repeated_bool;
+    private $repeated_enum;
+
+    public function getRepeatedInt32()
+    {
+        return $this->repeated_int32;
+    }
+
+    public function setRepeatedInt32(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::INT32);
+        $this->repeated_int32 = $var;
+    }
+
+    public function getRepeatedInt64()
+    {
+        return $this->repeated_int64;
+    }
+
+    public function setRepeatedInt64(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::INT64);
+        $this->repeated_int64 = $var;
+    }
+
+    public function getRepeatedUint32()
+    {
+        return $this->repeated_uint32;
+    }
+
+    public function setRepeatedUint32(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::UINT32);
+        $this->repeated_uint32 = $var;
+    }
+
+    public function getRepeatedUint64()
+    {
+        return $this->repeated_uint64;
+    }
+
+    public function setRepeatedUint64(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::UINT64);
+        $this->repeated_uint64 = $var;
+    }
+
+    public function getRepeatedSint32()
+    {
+        return $this->repeated_sint32;
+    }
+
+    public function setRepeatedSint32(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::SINT32);
+        $this->repeated_sint32 = $var;
+    }
+
+    public function getRepeatedSint64()
+    {
+        return $this->repeated_sint64;
+    }
+
+    public function setRepeatedSint64(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::SINT64);
+        $this->repeated_sint64 = $var;
+    }
+
+    public function getRepeatedFixed32()
+    {
+        return $this->repeated_fixed32;
+    }
+
+    public function setRepeatedFixed32(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::FIXED32);
+        $this->repeated_fixed32 = $var;
+    }
+
+    public function getRepeatedFixed64()
+    {
+        return $this->repeated_fixed64;
+    }
+
+    public function setRepeatedFixed64(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::FIXED64);
+        $this->repeated_fixed64 = $var;
+    }
+
+    public function getRepeatedSfixed32()
+    {
+        return $this->repeated_sfixed32;
+    }
+
+    public function setRepeatedSfixed32(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::SFIXED32);
+        $this->repeated_sfixed32 = $var;
+    }
+
+    public function getRepeatedSfixed64()
+    {
+        return $this->repeated_sfixed64;
+    }
+
+    public function setRepeatedSfixed64(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::SFIXED64);
+        $this->repeated_sfixed64 = $var;
+    }
+
+    public function getRepeatedFloat()
+    {
+        return $this->repeated_float;
+    }
+
+    public function setRepeatedFloat(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::FLOAT);
+        $this->repeated_float = $var;
+    }
+
+    public function getRepeatedDouble()
+    {
+        return $this->repeated_double;
+    }
+
+    public function setRepeatedDouble(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::DOUBLE);
+        $this->repeated_double = $var;
+    }
+
+    public function getRepeatedBool()
+    {
+        return $this->repeated_bool;
+    }
+
+    public function setRepeatedBool(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::BOOL);
+        $this->repeated_bool = $var;
+    }
+
+    public function getRepeatedEnum()
+    {
+        return $this->repeated_enum;
+    }
+
+    public function setRepeatedEnum(&$var)
+    {
+        GPBUtil::checkRepeatedField($var, GPBType::ENUM, Foo\TestEnum::class);
+        $this->repeated_enum = $var;
+    }
+
+}
+
+class TestEnum
+{
+    const ZERO = 0;
+    const ONE = 1;
+}
+
+$pool = DescriptorPool::getGeneratedPool();
+
+$pool->internalAddGeneratedFile(hex2bin(
+    "0a83250a0a746573742e70726f746f1203666f6f1a12746573745f696e63" .
+    "6c7564652e70726f746f22be1d0a0b546573744d65737361676512160a0e" .
+    "6f7074696f6e616c5f696e74333218012001280512160a0e6f7074696f6e" .
+    "616c5f696e74363418022001280312170a0f6f7074696f6e616c5f75696e" .
+    "74333218032001280d12170a0f6f7074696f6e616c5f75696e7436341804" .
+    "2001280412170a0f6f7074696f6e616c5f73696e74333218052001281112" .
+    "170a0f6f7074696f6e616c5f73696e74363418062001281212180a106f70" .
+    "74696f6e616c5f6669786564333218072001280712180a106f7074696f6e" .
+    "616c5f6669786564363418082001280612190a116f7074696f6e616c5f73" .
+    "6669786564333218092001280f12190a116f7074696f6e616c5f73666978" .
+    "65643634180a2001281012160a0e6f7074696f6e616c5f666c6f6174180b" .
+    "2001280212170a0f6f7074696f6e616c5f646f75626c65180c2001280112" .
+    "150a0d6f7074696f6e616c5f626f6f6c180d2001280812170a0f6f707469" .
+    "6f6e616c5f737472696e67180e2001280912160a0e6f7074696f6e616c5f" .
+    "6279746573180f2001280c12240a0d6f7074696f6e616c5f656e756d1810" .
+    "2001280e320d2e666f6f2e54657374456e756d122e0a106f7074696f6e61" .
+    "6c5f6d65737361676518112001280b32142e666f6f2e546573744d657373" .
+    "6167652e53756212330a196f7074696f6e616c5f696e636c756465645f6d" .
+    "65737361676518122001280b32102e6261722e54657374496e636c756465" .
+    "12230a0972656375727369766518132001280b32102e666f6f2e54657374" .
+    "4d65737361676512160a0e72657065617465645f696e743332181f200328" .
+    "0512160a0e72657065617465645f696e74363418202003280312170a0f72" .
+    "657065617465645f75696e74333218212003280d12170a0f726570656174" .
+    "65645f75696e74363418222003280412170a0f72657065617465645f7369" .
+    "6e74333218232003281112170a0f72657065617465645f73696e74363418" .
+    "242003281212180a1072657065617465645f666978656433321825200328" .
+    "0712180a1072657065617465645f6669786564363418262003280612190a" .
+    "1172657065617465645f736669786564333218272003280f12190a117265" .
+    "7065617465645f736669786564363418282003281012160a0e7265706561" .
+    "7465645f666c6f617418292003280212170a0f72657065617465645f646f" .
+    "75626c65182a2003280112150a0d72657065617465645f626f6f6c182b20" .
+    "03280812170a0f72657065617465645f737472696e67182c200328091216" .
+    "0a0e72657065617465645f6279746573182d2003280c12240a0d72657065" .
+    "617465645f656e756d182e2003280e320d2e666f6f2e54657374456e756d" .
+    "122e0a1072657065617465645f6d657373616765182f2003280b32142e66" .
+    "6f6f2e546573744d6573736167652e537562122c0a127265706561746564" .
+    "5f72656375727369766518302003280b32102e666f6f2e546573744d6573" .
+    "7361676512150a0b6f6e656f665f696e743332183320012805480012150a" .
+    "0b6f6e656f665f696e743634183420012803480012160a0c6f6e656f665f" .
+    "75696e74333218352001280d480012160a0c6f6e656f665f75696e743634" .
+    "183620012804480012160a0c6f6e656f665f73696e74333218372001280d" .
+    "480012160a0c6f6e656f665f73696e743634183820012804480012170a0d" .
+    "6f6e656f665f6669786564333218392001280d480012170a0d6f6e656f66" .
+    "5f66697865643634183a20012804480012180a0e6f6e656f665f73666978" .
+    "65643332183b2001280d480012180a0e6f6e656f665f7366697865643634" .
+    "183c20012804480012160a0c6f6e656f665f646f75626c65183d20012801" .
+    "480012150a0b6f6e656f665f666c6f6174183e20012802480012140a0a6f" .
+    "6e656f665f626f6f6c183f20012808480012160a0c6f6e656f665f737472" .
+    "696e67184020012809480012150a0b6f6e656f665f627974657318412001" .
+    "280c480012230a0a6f6e656f665f656e756d18422001280e320d2e666f6f" .
+    "2e54657374456e756d4800122d0a0d6f6e656f665f6d6573736167651843" .
+    "2001280b32142e666f6f2e546573744d6573736167652e5375624800123c" .
+    "0a0f6d61705f696e7433325f696e74333218472003280b32232e666f6f2e" .
+    "546573744d6573736167652e4d6170496e743332496e743332456e747279" .
+    "123c0a0f6d61705f696e7436345f696e74363418482003280b32232e666f" .
+    "6f2e546573744d6573736167652e4d6170496e743634496e743634456e74" .
+    "727912400a116d61705f75696e7433325f75696e74333218492003280b32" .
+    "252e666f6f2e546573744d6573736167652e4d617055696e74333255696e" .
+    "743332456e74727912400a116d61705f75696e7436345f75696e74363418" .
+    "4a2003280b32252e666f6f2e546573744d6573736167652e4d617055696e" .
+    "74363455696e743634456e74727912400a116d61705f73696e7433325f73" .
+    "696e743332184b2003280b32252e666f6f2e546573744d6573736167652e" .
+    "4d617053696e74333253696e743332456e74727912400a116d61705f7369" .
+    "6e7436345f73696e743634184c2003280b32252e666f6f2e546573744d65" .
+    "73736167652e4d617053696e74363453696e743634456e74727912440a13" .
+    "6d61705f666978656433325f66697865643332184d2003280b32272e666f" .
+    "6f2e546573744d6573736167652e4d617046697865643332466978656433" .
+    "32456e74727912440a136d61705f666978656436345f6669786564363418" .
+    "4e2003280b32272e666f6f2e546573744d6573736167652e4d6170466978" .
+    "6564363446697865643634456e74727912480a156d61705f736669786564" .
+    "33325f7366697865643332184f2003280b32292e666f6f2e546573744d65" .
+    "73736167652e4d617053666978656433325366697865643332456e747279" .
+    "12480a156d61705f73666978656436345f73666978656436341850200328" .
+    "0b32292e666f6f2e546573744d6573736167652e4d617053666978656436" .
+    "345366697865643634456e747279123c0a0f6d61705f696e7433325f666c" .
+    "6f617418512003280b32232e666f6f2e546573744d6573736167652e4d61" .
+    "70496e743332466c6f6174456e747279123e0a106d61705f696e7433325f" .
+    "646f75626c6518522003280b32242e666f6f2e546573744d657373616765" .
+    "2e4d6170496e743332446f75626c65456e74727912380a0d6d61705f626f" .
+    "6f6c5f626f6f6c18532003280b32212e666f6f2e546573744d6573736167" .
+    "652e4d6170426f6f6c426f6f6c456e74727912400a116d61705f73747269" .
+    "6e675f737472696e6718542003280b32252e666f6f2e546573744d657373" .
+    "6167652e4d6170537472696e67537472696e67456e747279123c0a0f6d61" .
+    "705f696e7433325f627974657318552003280b32232e666f6f2e54657374" .
+    "4d6573736167652e4d6170496e7433324279746573456e747279123a0a0e" .
+    "6d61705f696e7433325f656e756d18562003280b32222e666f6f2e546573" .
+    "744d6573736167652e4d6170496e743332456e756d456e74727912400a11" .
+    "6d61705f696e7433325f6d65737361676518572003280b32252e666f6f2e" .
+    "546573744d6573736167652e4d6170496e7433324d657373616765456e74" .
+    "727912390a0d6d61705f72656375727369766518582003280b32222e666f" .
+    "6f2e546573744d6573736167652e4d6170526563757273697665456e7472" .
+    "791a340a124d6170496e743332496e743332456e747279120b0a036b6579" .
+    "180120012805120d0a0576616c75651802200128053a0238011a340a124d" .
+    "6170496e743634496e743634456e747279120b0a036b6579180120012803" .
+    "120d0a0576616c75651802200128033a0238011a360a144d617055696e74" .
+    "333255696e743332456e747279120b0a036b657918012001280d120d0a05" .
+    "76616c756518022001280d3a0238011a360a144d617055696e7436345569" .
+    "6e743634456e747279120b0a036b6579180120012804120d0a0576616c75" .
+    "651802200128043a0238011a360a144d617053696e74333253696e743332" .
+    "456e747279120b0a036b6579180120012811120d0a0576616c7565180220" .
+    "0128113a0238011a360a144d617053696e74363453696e743634456e7472" .
+    "79120b0a036b6579180120012812120d0a0576616c75651802200128123a" .
+    "0238011a380a164d61704669786564333246697865643332456e74727912" .
+    "0b0a036b6579180120012807120d0a0576616c75651802200128073a0238" .
+    "011a380a164d61704669786564363446697865643634456e747279120b0a" .
+    "036b6579180120012806120d0a0576616c75651802200128063a0238011a" .
+    "3a0a184d617053666978656433325366697865643332456e747279120b0a" .
+    "036b657918012001280f120d0a0576616c756518022001280f3a0238011a" .
+    "3a0a184d617053666978656436345366697865643634456e747279120b0a" .
+    "036b6579180120012810120d0a0576616c75651802200128103a0238011a" .
+    "340a124d6170496e743332466c6f6174456e747279120b0a036b65791801" .
+    "20012805120d0a0576616c75651802200128023a0238011a350a134d6170" .
+    "496e743332446f75626c65456e747279120b0a036b657918012001280512" .
+    "0d0a0576616c75651802200128013a0238011a320a104d6170426f6f6c42" .
+    "6f6f6c456e747279120b0a036b6579180120012808120d0a0576616c7565" .
+    "1802200128083a0238011a360a144d6170537472696e67537472696e6745" .
+    "6e747279120b0a036b6579180120012809120d0a0576616c756518022001" .
+    "28093a0238011a340a124d6170496e7433324279746573456e747279120b" .
+    "0a036b6579180120012805120d0a0576616c756518022001280c3a023801" .
+    "1a420a114d6170496e743332456e756d456e747279120b0a036b65791801" .
+    "20012805121c0a0576616c756518022001280e320d2e666f6f2e54657374" .
+    "456e756d3a0238011a4c0a144d6170496e7433324d657373616765456e74" .
+    "7279120b0a036b657918012001280512230a0576616c756518022001280b" .
+    "32142e666f6f2e546573744d6573736167652e5375623a0238011a450a11" .
+    "4d6170526563757273697665456e747279120b0a036b6579180120012805" .
+    "121f0a0576616c756518022001280b32102e666f6f2e546573744d657373" .
+    "6167653a0238011a100a0353756212090a0161180120012805420a0a086d" .
+    "795f6f6e656f6622b7030a11546573745061636b65644d65737361676512" .
+    "1a0a0e72657065617465645f696e743332185a2003280542021001121a0a" .
+    "0e72657065617465645f696e743634185b2003280342021001121b0a0f72" .
+    "657065617465645f75696e743332185c2003280d42021001121b0a0f7265" .
+    "7065617465645f75696e743634185d2003280442021001121b0a0f726570" .
+    "65617465645f73696e743332185e2003281142021001121b0a0f72657065" .
+    "617465645f73696e743634185f2003281242021001121c0a107265706561" .
+    "7465645f6669786564333218602003280742021001121c0a107265706561" .
+    "7465645f6669786564363418612003280642021001121d0a117265706561" .
+    "7465645f736669786564333218622003280f42021001121d0a1172657065" .
+    "617465645f736669786564363418632003281042021001121a0a0e726570" .
+    "65617465645f666c6f617418642003280242021001121b0a0f7265706561" .
+    "7465645f646f75626c651865200328014202100112190a0d726570656174" .
+    "65645f626f6f6c1866200328084202100112280a0d72657065617465645f" .
+    "656e756d18672003280e320d2e666f6f2e54657374456e756d4202100122" .
+    "b9030a1354657374556e7061636b65644d657373616765121a0a0e726570" .
+    "65617465645f696e743332185a2003280542021000121a0a0e7265706561" .
+    "7465645f696e743634185b2003280342021000121b0a0f72657065617465" .
+    "645f75696e743332185c2003280d42021000121b0a0f7265706561746564" .
+    "5f75696e743634185d2003280442021000121b0a0f72657065617465645f" .
+    "73696e743332185e2003281142021000121b0a0f72657065617465645f73" .
+    "696e743634185f2003281242021000121c0a1072657065617465645f6669" .
+    "786564333218602003280742021000121c0a1072657065617465645f6669" .
+    "786564363418612003280642021000121d0a1172657065617465645f7366" .
+    "69786564333218622003280f42021000121d0a1172657065617465645f73" .
+    "6669786564363418632003281042021000121a0a0e72657065617465645f" .
+    "666c6f617418642003280242021000121b0a0f72657065617465645f646f" .
+    "75626c651865200328014202100012190a0d72657065617465645f626f6f" .
+    "6c1866200328084202100012280a0d72657065617465645f656e756d1867" .
+    "2003280e320d2e666f6f2e54657374456e756d420210002a1d0a08546573" .
+    "74456e756d12080a045a45524f100012070a034f4e451001620670726f74" .
+    "6f33"
+));
+
diff --git a/php/tests/test.proto b/php/tests/test.proto
new file mode 100644
index 0000000..15709c8
--- /dev/null
+++ b/php/tests/test.proto
@@ -0,0 +1,136 @@
+syntax = "proto3";
+
+import 'test_include.proto';
+
+package foo;
+
+message TestMessage {
+  // Singular
+  int32 optional_int32 = 1;
+  int64 optional_int64 = 2;
+  uint32 optional_uint32 = 3;
+  uint64 optional_uint64 = 4;
+  sint32 optional_sint32 = 5;
+  sint64 optional_sint64 = 6;
+  fixed32 optional_fixed32 = 7;
+  fixed64 optional_fixed64 = 8;
+  sfixed32 optional_sfixed32 = 9;
+  sfixed64 optional_sfixed64 = 10;
+  float optional_float = 11;
+  double optional_double = 12;
+  bool optional_bool = 13;
+  string optional_string = 14;
+  bytes optional_bytes = 15;
+
+  TestEnum optional_enum = 16;
+  Sub optional_message = 17;
+  bar.TestInclude optional_included_message = 18;
+  TestMessage recursive = 19;
+
+  // Repeated
+  repeated    int32 repeated_int32    = 31;
+  repeated    int64 repeated_int64    = 32;
+  repeated   uint32 repeated_uint32   = 33;
+  repeated   uint64 repeated_uint64   = 34;
+  repeated   sint32 repeated_sint32   = 35;
+  repeated   sint64 repeated_sint64   = 36;
+  repeated  fixed32 repeated_fixed32  = 37;
+  repeated  fixed64 repeated_fixed64  = 38;
+  repeated sfixed32 repeated_sfixed32 = 39;
+  repeated sfixed64 repeated_sfixed64 = 40;
+  repeated    float repeated_float    = 41;
+  repeated   double repeated_double   = 42;
+  repeated     bool repeated_bool     = 43;
+  repeated   string repeated_string   = 44;
+  repeated    bytes repeated_bytes    = 45;
+
+  repeated TestEnum repeated_enum = 46;
+  repeated Sub repeated_message = 47;
+  repeated TestMessage repeated_recursive = 48;
+
+  oneof my_oneof {
+    int32  oneof_int32    = 51;
+    int64  oneof_int64    = 52;
+    uint32 oneof_uint32   = 53;
+    uint64 oneof_uint64   = 54;
+    uint32 oneof_sint32   = 55;
+    uint64 oneof_sint64   = 56;
+    uint32 oneof_fixed32  = 57;
+    uint64 oneof_fixed64  = 58;
+    uint32 oneof_sfixed32 = 59;
+    uint64 oneof_sfixed64 = 60;
+    double oneof_double   = 61;
+    float  oneof_float    = 62;
+    bool   oneof_bool     = 63;
+    string oneof_string   = 64;
+    bytes  oneof_bytes    = 65;
+    TestEnum oneof_enum   = 66;
+    Sub    oneof_message  = 67;
+  }
+
+  map<int32,       int32> map_int32_int32       = 71;
+  map<int64,       int64> map_int64_int64       = 72;
+  map<uint32,     uint32> map_uint32_uint32     = 73;
+  map<uint64,     uint64> map_uint64_uint64     = 74;
+  map<sint32,     sint32> map_sint32_sint32     = 75;
+  map<sint64,     sint64> map_sint64_sint64     = 76;
+  map<fixed32,   fixed32> map_fixed32_fixed32   = 77;
+  map<fixed64,   fixed64> map_fixed64_fixed64   = 78;
+  map<sfixed32, sfixed32> map_sfixed32_sfixed32 = 79;
+  map<sfixed64, sfixed64> map_sfixed64_sfixed64 = 80;
+  map<int32,       float> map_int32_float       = 81;
+  map<int32,      double> map_int32_double      = 82;
+  map<bool,         bool> map_bool_bool         = 83;
+  map<string,     string> map_string_string     = 84;
+  map<int32,       bytes> map_int32_bytes       = 85;
+  map<int32,    TestEnum> map_int32_enum        = 86;
+  map<int32,         Sub> map_int32_message     = 87;
+
+  map<int32, TestMessage> map_recursive = 88;
+
+  message Sub {
+    int32 a = 1;
+  }
+
+  // NestedMessage nested_message = 90;
+}
+
+enum TestEnum {
+  ZERO = 0;
+  ONE  = 1;
+}
+
+message TestPackedMessage {
+  repeated int32    repeated_int32    = 90  [packed = true];
+  repeated int64    repeated_int64    = 91  [packed = true];
+  repeated uint32   repeated_uint32   = 92  [packed = true];
+  repeated uint64   repeated_uint64   = 93  [packed = true];
+  repeated sint32   repeated_sint32   = 94  [packed = true];
+  repeated sint64   repeated_sint64   = 95  [packed = true];
+  repeated fixed32  repeated_fixed32  = 96  [packed = true];
+  repeated fixed64  repeated_fixed64  = 97  [packed = true];
+  repeated sfixed32 repeated_sfixed32 = 98  [packed = true];
+  repeated sfixed64 repeated_sfixed64 = 99  [packed = true];
+  repeated float    repeated_float    = 100 [packed = true];
+  repeated double   repeated_double   = 101 [packed = true];
+  repeated bool     repeated_bool     = 102 [packed = true];
+  repeated TestEnum repeated_enum     = 103 [packed = true];
+}
+
+// Need to be in sync with TestPackedMessage.
+message TestUnpackedMessage {
+  repeated int32    repeated_int32    = 90  [packed = false];
+  repeated int64    repeated_int64    = 91  [packed = false];
+  repeated uint32   repeated_uint32   = 92  [packed = false];
+  repeated uint64   repeated_uint64   = 93  [packed = false];
+  repeated sint32   repeated_sint32   = 94  [packed = false];
+  repeated sint64   repeated_sint64   = 95  [packed = false];
+  repeated fixed32  repeated_fixed32  = 96  [packed = false];
+  repeated fixed64  repeated_fixed64  = 97  [packed = false];
+  repeated sfixed32 repeated_sfixed32 = 98  [packed = false];
+  repeated sfixed64 repeated_sfixed64 = 99  [packed = false];
+  repeated float    repeated_float    = 100 [packed = false];
+  repeated double   repeated_double   = 101 [packed = false];
+  repeated bool     repeated_bool     = 102 [packed = false];
+  repeated TestEnum repeated_enum     = 103 [packed = false];
+}
diff --git a/php/tests/test.sh b/php/tests/test.sh
new file mode 100755
index 0000000..52833ed
--- /dev/null
+++ b/php/tests/test.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+cd ../ext/google/protobuf/
+make clean
+set -e
+
+phpize && ./configure --enable-debug CFLAGS='-g -O0' && make
+cd -
+
+tests=( array_test.php encode_decode_test.php generated_class_test.php map_field_test.php )
+
+for t in "${tests[@]}"
+do
+  echo "****************************"
+  echo "* $t"
+  echo "****************************"
+  php -dextension=../ext/google/protobuf/modules/protobuf.so `which phpunit` $t
+  echo ""
+done
+
+# Make sure to run the memory test in debug mode.
+php -dextension=../ext/google/protobuf/modules/protobuf.so memory_leak_test.php
+
+USE_ZEND_ALLOC=0 valgrind --leak-check=yes php -dextension=../ext/google/protobuf/modules/protobuf.so memory_leak_test.php
diff --git a/php/tests/test_base.php b/php/tests/test_base.php
new file mode 100644
index 0000000..25f18f7
--- /dev/null
+++ b/php/tests/test_base.php
@@ -0,0 +1,92 @@
+<?php
+
+use Foo\TestMessage;
+use Foo\TestMessage_Sub;
+
+class TestBase extends PHPUnit_Framework_TestCase
+{
+
+    public function setFields(TestMessage $m)
+    {
+        TestUtil::setTestMessage($m);
+    }
+
+    public function expectFields(TestMessage $m)
+    {
+        $this->assertSame(-42,  $m->getOptionalInt32());
+        $this->assertSame(42,   $m->getOptionalUint32());
+        $this->assertSame(-43,  $m->getOptionalInt64());
+        $this->assertSame(43,   $m->getOptionalUint64());
+        $this->assertSame(-44,  $m->getOptionalSint32());
+        $this->assertSame(-45,  $m->getOptionalSint64());
+        $this->assertSame(46,   $m->getOptionalFixed32());
+        $this->assertSame(47,   $m->getOptionalFixed64());
+        $this->assertSame(-46,  $m->getOptionalSfixed32());
+        $this->assertSame(-47,  $m->getOptionalSfixed64());
+        $this->assertSame(1.5,  $m->getOptionalFloat());
+        $this->assertSame(1.6,  $m->getOptionalDouble());
+        $this->assertSame(true, $m->getOptionalBool());
+        $this->assertSame('a',  $m->getOptionalString());
+        $this->assertSame('b',  $m->getOptionalBytes());
+        $this->assertSame(33,   $m->getOptionalMessage()->getA());
+
+        $this->assertEquals(-42,  $m->getRepeatedInt32()[0]);
+        $this->assertEquals(42,   $m->getRepeatedUint32()[0]);
+        $this->assertEquals(-43,  $m->getRepeatedInt64()[0]);
+        $this->assertEquals(43,   $m->getRepeatedUint64()[0]);
+        $this->assertEquals(-44,  $m->getRepeatedSint32()[0]);
+        $this->assertEquals(-45,  $m->getRepeatedSint64()[0]);
+        $this->assertEquals(46,   $m->getRepeatedFixed32()[0]);
+        $this->assertEquals(47,   $m->getRepeatedFixed64()[0]);
+        $this->assertEquals(-46,  $m->getRepeatedSfixed32()[0]);
+        $this->assertEquals(-47,  $m->getRepeatedSfixed64()[0]);
+        $this->assertEquals(1.5,  $m->getRepeatedFloat()[0]);
+        $this->assertEquals(1.6,  $m->getRepeatedDouble()[0]);
+        $this->assertEquals(true, $m->getRepeatedBool()[0]);
+        $this->assertEquals('a',  $m->getRepeatedString()[0]);
+        $this->assertEquals('b',  $m->getRepeatedBytes()[0]);
+        $this->assertEquals(34,   $m->getRepeatedMessage()[0]->GetA());
+
+        $this->assertEquals(-52,   $m->getRepeatedInt32()[1]);
+        $this->assertEquals(52,    $m->getRepeatedUint32()[1]);
+        $this->assertEquals(-53,   $m->getRepeatedInt64()[1]);
+        $this->assertEquals(53,    $m->getRepeatedUint64()[1]);
+        $this->assertEquals(-54,   $m->getRepeatedSint32()[1]);
+        $this->assertEquals(-55,   $m->getRepeatedSint64()[1]);
+        $this->assertEquals(56,    $m->getRepeatedFixed32()[1]);
+        $this->assertEquals(57,    $m->getRepeatedFixed64()[1]);
+        $this->assertEquals(-56,   $m->getRepeatedSfixed32()[1]);
+        $this->assertEquals(-57,   $m->getRepeatedSfixed64()[1]);
+        $this->assertEquals(2.5,   $m->getRepeatedFloat()[1]);
+        $this->assertEquals(2.6,   $m->getRepeatedDouble()[1]);
+        $this->assertEquals(false, $m->getRepeatedBool()[1]);
+        $this->assertEquals('c',   $m->getRepeatedString()[1]);
+        $this->assertEquals('d',   $m->getRepeatedBytes()[1]);
+        $this->assertEquals(35,    $m->getRepeatedMessage()[1]->GetA());
+    }
+
+    public function expectEmptyFields(TestMessage $m)
+    {
+        $this->assertSame(0,   $m->getOptionalInt32());
+        $this->assertSame(0,   $m->getOptionalUint32());
+        $this->assertSame(0,   $m->getOptionalInt64());
+        $this->assertSame(0,   $m->getOptionalUint64());
+        $this->assertSame(0,   $m->getOptionalSint32());
+        $this->assertSame(0,   $m->getOptionalSint64());
+        $this->assertSame(0,   $m->getOptionalFixed32());
+        $this->assertSame(0,   $m->getOptionalFixed64());
+        $this->assertSame(0,   $m->getOptionalSfixed32());
+        $this->assertSame(0,   $m->getOptionalSfixed64());
+        $this->assertSame(0.0, $m->getOptionalFloat());
+        $this->assertSame(0.0, $m->getOptionalDouble());
+        $this->assertSame(false, $m->getOptionalBool());
+        $this->assertSame('',  $m->getOptionalString());
+        $this->assertSame('',  $m->getOptionalBytes());
+        $this->assertNull($m->getOptionalMessage());
+    }
+
+  // This test is to avoid the warning of no test by php unit.
+  public function testNone()
+  {
+  }
+}
diff --git a/php/tests/test_include.pb.php b/php/tests/test_include.pb.php
new file mode 100644
index 0000000..2c43cc4
--- /dev/null
+++ b/php/tests/test_include.pb.php
@@ -0,0 +1,36 @@
+<?php
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: test_include.proto
+
+namespace Bar;
+
+use Google\Protobuf\Internal\DescriptorPool;
+use Google\Protobuf\Internal\GPBType;
+use Google\Protobuf\Internal\RepeatedField;
+use Google\Protobuf\Internal\GPBUtil;
+
+class TestInclude extends \Google\Protobuf\Internal\Message
+{
+    private $a = 0;
+
+    public function getA()
+    {
+        return $this->a;
+    }
+
+    public function setA($var)
+    {
+        GPBUtil::checkInt32($var);
+        $this->a = $var;
+    }
+
+}
+
+$pool = DescriptorPool::getGeneratedPool();
+
+$pool->internalAddGeneratedFile(hex2bin(
+    "0a3b0a12746573745f696e636c7564652e70726f746f120362617222180a" .
+    "0b54657374496e636c75646512090a0161180120012805620670726f746f" .
+    "33"
+));
+
diff --git a/php/tests/test_util.php b/php/tests/test_util.php
new file mode 100644
index 0000000..decd1a7
--- /dev/null
+++ b/php/tests/test_util.php
@@ -0,0 +1,393 @@
+<?php
+
+use Foo\TestEnum;
+use Foo\TestMessage;
+use Foo\TestMessage_Sub;
+use Foo\TestPackedMessage;
+use Foo\TestUnpackedMessage;
+
+define('MAX_FLOAT_DIFF', 0.000001);
+
+if (PHP_INT_SIZE == 8) {
+    define('MAX_INT_STRING', '9223372036854775807');
+    define('MAX_INT_UPPER_STRING', '9223372036854775808');
+} else {
+    define('MAX_INT_STRING', '2147483647');
+    define('MAX_INT_UPPER_STRING', '2147483648');
+}
+
+define('MAX_INT32', 2147483647);
+define('MAX_INT32_FLOAT', 2147483647.0);
+define('MAX_INT32_STRING', '2147483647');
+
+define('MIN_INT32', -2147483648);
+define('MIN_INT32_FLOAT', -2147483648.0);
+define('MIN_INT32_STRING', '-2147483648');
+
+define('MAX_UINT32', 4294967295);
+define('MAX_UINT32_FLOAT', 4294967295.0);
+define('MAX_UINT32_STRING', '4294967295');
+
+define('MIN_UINT32', -2147483648);
+define('MIN_UINT32_FLOAT', -2147483648.0);
+define('MIN_UINT32_STRING', '-2147483648');
+
+define('MAX_INT64', 9223372036854775807);
+define('MAX_INT64_STRING', '9223372036854775807');
+
+define('MIN_INT64_STRING', '-9223372036854775808');
+if (PHP_INT_SIZE === 8) {
+    define('MIN_INT64', -9223372036854775808);
+} else {
+    define('MIN_INT64', MIN_INT64_STRING);
+}
+
+define('MAX_UINT64_STRING', '-9223372036854775808');
+define('MAX_UINT64', MAX_UINT64_STRING);
+
+class TestUtil
+{
+
+    public static function setTestMessage(TestMessage $m)
+    {
+        $m->setOptionalInt32(-42);
+        $m->setOptionalInt64(-43);
+        $m->setOptionalUint32(42);
+        $m->setOptionalUint64(43);
+        $m->setOptionalSint32(-44);
+        $m->setOptionalSint64(-45);
+        $m->setOptionalFixed32(46);
+        $m->setOptionalFixed64(47);
+        $m->setOptionalSfixed32(-46);
+        $m->setOptionalSfixed64(-47);
+        $m->setOptionalFloat(1.5);
+        $m->setOptionalDouble(1.6);
+        $m->setOptionalBool(true);
+        $m->setOptionalString('a');
+        $m->setOptionalBytes('b');
+        $m->setOptionalEnum(TestEnum::ONE);
+        $m->setOptionalMessage(new TestMessage_Sub());
+        $m->getOptionalMessage()->SetA(33);
+
+        $m->getRepeatedInt32()    []= -42;
+        $m->getRepeatedInt64()    []= -43;
+        $m->getRepeatedUint32()   []=  42;
+        $m->getRepeatedUint64()   []=  43;
+        $m->getRepeatedSint32()   []= -44;
+        $m->getRepeatedSint64()   []= -45;
+        $m->getRepeatedFixed32()  []=  46;
+        $m->getRepeatedFixed64()  []=  47;
+        $m->getRepeatedSfixed32() []= -46;
+        $m->getRepeatedSfixed64() []= -47;
+        $m->getRepeatedFloat()    []= 1.5;
+        $m->getRepeatedDouble()   []= 1.6;
+        $m->getRepeatedBool()     []= true;
+        $m->getRepeatedString()   []= 'a';
+        $m->getRepeatedBytes()    []= 'b';
+        $m->getRepeatedEnum()     []= TestEnum::ZERO;
+        $m->getRepeatedMessage()  []= new TestMessage_Sub();
+        $m->getRepeatedMessage()[0]->setA(34);
+
+        $m->getRepeatedInt32()    []= -52;
+        $m->getRepeatedInt64()    []= -53;
+        $m->getRepeatedUint32()   []=  52;
+        $m->getRepeatedUint64()   []=  53;
+        $m->getRepeatedSint32()   []= -54;
+        $m->getRepeatedSint64()   []= -55;
+        $m->getRepeatedFixed32()  []=  56;
+        $m->getRepeatedFixed64()  []=  57;
+        $m->getRepeatedSfixed32() []= -56;
+        $m->getRepeatedSfixed64() []= -57;
+        $m->getRepeatedFloat()    []= 2.5;
+        $m->getRepeatedDouble()   []= 2.6;
+        $m->getRepeatedBool()     []= false;
+        $m->getRepeatedString()   []= 'c';
+        $m->getRepeatedBytes()    []= 'd';
+        $m->getRepeatedEnum()     []= TestEnum::ONE;
+        $m->getRepeatedMessage()  []= new TestMessage_Sub();
+        $m->getRepeatedMessage()[1]->SetA(35);
+
+        $m->getMapInt32Int32()[-62] = -62;
+        $m->getMapInt64Int64()[-63] = -63;
+        $m->getMapUint32Uint32()[62] = 62;
+        $m->getMapUint64Uint64()[63] = 63;
+        $m->getMapSint32Sint32()[-64] = -64;
+        $m->getMapSint64Sint64()[-65] = -65;
+        $m->getMapFixed32Fixed32()[66] = 66;
+        $m->getMapFixed64Fixed64()[67] = 67;
+        $m->getMapInt32Float()[1] = 3.5;
+        $m->getMapInt32Double()[1] = 3.6;
+        $m->getMapBoolBool()[true] = true;
+        $m->getMapStringString()['e'] = 'e';
+        $m->getMapInt32Bytes()[1] = 'f';
+        $m->getMapInt32Enum()[1] = TestEnum::ONE;
+        $m->getMapInt32Message()[1] = new TestMessage_Sub();
+        $m->getMapInt32Message()[1]->SetA(36);
+    }
+
+    public static function assertTestMessage(TestMessage $m)
+    {
+        assert(-42 === $m->getOptionalInt32());
+        assert(42  === $m->getOptionalUint32());
+        assert(-43 === $m->getOptionalInt64());
+        assert(43  === $m->getOptionalUint64());
+        assert(-44 === $m->getOptionalSint32());
+        assert(-45 === $m->getOptionalSint64());
+        assert(46  === $m->getOptionalFixed32());
+        assert(47  === $m->getOptionalFixed64());
+        assert(-46 === $m->getOptionalSfixed32());
+        assert(-47 === $m->getOptionalSfixed64());
+        assert(1.5 === $m->getOptionalFloat());
+        assert(1.6 === $m->getOptionalDouble());
+        assert(true=== $m->getOptionalBool());
+        assert('a' === $m->getOptionalString());
+        assert('b' === $m->getOptionalBytes());
+        assert(TestEnum::ONE === $m->getOptionalEnum());
+        assert(33  === $m->getOptionalMessage()->getA());
+
+        assert(-42 === $m->getRepeatedInt32()[0]);
+        assert(42  === $m->getRepeatedUint32()[0]);
+        assert(-43 === $m->getRepeatedInt64()[0]);
+        assert(43  === $m->getRepeatedUint64()[0]);
+        assert(-44 === $m->getRepeatedSint32()[0]);
+        assert(-45 === $m->getRepeatedSint64()[0]);
+        assert(46  === $m->getRepeatedFixed32()[0]);
+        assert(47  === $m->getRepeatedFixed64()[0]);
+        assert(-46 === $m->getRepeatedSfixed32()[0]);
+        assert(-47 === $m->getRepeatedSfixed64()[0]);
+        assert(1.5 === $m->getRepeatedFloat()[0]);
+        assert(1.6 === $m->getRepeatedDouble()[0]);
+        assert(true=== $m->getRepeatedBool()[0]);
+        assert('a' === $m->getRepeatedString()[0]);
+        assert('b' === $m->getRepeatedBytes()[0]);
+        assert(TestEnum::ZERO === $m->getRepeatedEnum()[0]);
+        assert(34  === $m->getRepeatedMessage()[0]->getA());
+
+        assert(-52 === $m->getRepeatedInt32()[1]);
+        assert(52  === $m->getRepeatedUint32()[1]);
+        assert(-53 === $m->getRepeatedInt64()[1]);
+        assert(53  === $m->getRepeatedUint64()[1]);
+        assert(-54 === $m->getRepeatedSint32()[1]);
+        assert(-55 === $m->getRepeatedSint64()[1]);
+        assert(56  === $m->getRepeatedFixed32()[1]);
+        assert(57  === $m->getRepeatedFixed64()[1]);
+        assert(-56 === $m->getRepeatedSfixed32()[1]);
+        assert(-57 === $m->getRepeatedSfixed64()[1]);
+        assert(2.5 === $m->getRepeatedFloat()[1]);
+        assert(2.6 === $m->getRepeatedDouble()[1]);
+        assert(false === $m->getRepeatedBool()[1]);
+        assert('c' === $m->getRepeatedString()[1]);
+        assert('d' === $m->getRepeatedBytes()[1]);
+        assert(TestEnum::ONE === $m->getRepeatedEnum()[1]);
+        assert(35  === $m->getRepeatedMessage()[1]->getA());
+
+        assert(-62 === $m->getMapInt32Int32()[-62]);
+        assert(-63 === $m->getMapInt64Int64()[-63]);
+        assert(62  === $m->getMapUint32Uint32()[62]);
+        assert(63  === $m->getMapUint64Uint64()[63]);
+        assert(-64 === $m->getMapSint32Sint32()[-64]);
+        assert(-65 === $m->getMapSint64Sint64()[-65]);
+        assert(66  === $m->getMapFixed32Fixed32()[66]);
+        assert(67  === $m->getMapFixed64Fixed64()[67]);
+        assert(3.5 === $m->getMapInt32Float()[1]);
+        assert(3.6 === $m->getMapInt32Double()[1]);
+        assert(true === $m->getMapBoolBool()[true]);
+        assert('e' === $m->getMapStringString()['e']);
+        assert('f' === $m->getMapInt32Bytes()[1]);
+        assert(TestEnum::ONE === $m->getMapInt32Enum()[1]);
+        assert(36  === $m->getMapInt32Message()[1]->GetA());
+    }
+
+    public static function getGoldenTestMessage()
+    {
+        return hex2bin(
+            "08D6FFFFFF0F" .
+            "10D5FFFFFFFFFFFFFFFF01" .
+            "182A" .
+            "202B" .
+            "2857" .
+            "3059" .
+            "3D2E000000" .
+            "412F00000000000000" .
+            "4DD2FFFFFF" .
+            "51D1FFFFFFFFFFFFFF" .
+            "5D0000C03F" .
+            "619A9999999999F93F" .
+            "6801" .
+            "720161" .
+            "7A0162" .
+            "800101" .
+            "8A01020821" .
+
+            "F801D6FFFFFF0F" .
+            "F801CCFFFFFF0F" .
+            "8002D5FFFFFFFFFFFFFFFF01" .
+            "8002CBFFFFFFFFFFFFFFFF01" .
+            "88022A" .
+            "880234" .
+            "90022B" .
+            "900235" .
+            "980257" .
+            "98026B" .
+            "A00259" .
+            "A0026D" .
+            "AD022E000000" .
+            "AD0238000000" .
+            "B1022F00000000000000" .
+            "B1023900000000000000" .
+            "BD02D2FFFFFF" .
+            "BD02C8FFFFFF" .
+            "C102D1FFFFFFFFFFFFFF" .
+            "C102C7FFFFFFFFFFFFFF" .
+            "CD020000C03F" .
+            "CD0200002040" .
+            "D1029A9999999999F93F" .
+            "D102CDCCCCCCCCCC0440" .
+            "D80201" .
+            "D80200" .
+            "E2020161" .
+            "E2020163" .
+            "EA020162" .
+            "EA020164" .
+            "F00200" .
+            "F00201" .
+            "FA02020822" .
+            "FA02020823" .
+
+            "BA040C08C2FFFFFF0F10C2FFFFFF0F" .
+            "C2041608C1FFFFFFFFFFFFFFFF0110C1FFFFFFFFFFFFFFFF01" .
+            "CA0404083E103E" .
+            "D20404083F103F" .
+            "DA0404087f107F" .
+            "E20406088101108101" .
+            "EA040A0D420000001542000000" .
+            "F20412094300000000000000114300000000000000" .
+            "8A050708011500006040" .
+            "92050B080111CDCCCCCCCCCC0C40" .
+            "9A050408011001" .
+            "A205060a0165120165" .
+            "AA05050801120166" .
+            "B2050408011001" .
+            "Ba0506080112020824"
+        );
+    }
+
+    public static function setTestPackedMessage($m)
+    {
+        $m->getRepeatedInt32()[] = -42;
+        $m->getRepeatedInt32()[] = -52;
+        $m->getRepeatedInt64()[] = -43;
+        $m->getRepeatedInt64()[] = -53;
+        $m->getRepeatedUint32()[] = 42;
+        $m->getRepeatedUint32()[] = 52;
+        $m->getRepeatedUint64()[] = 43;
+        $m->getRepeatedUint64()[] = 53;
+        $m->getRepeatedSint32()[] = -44;
+        $m->getRepeatedSint32()[] = -54;
+        $m->getRepeatedSint64()[] = -45;
+        $m->getRepeatedSint64()[] = -55;
+        $m->getRepeatedFixed32()[] = 46;
+        $m->getRepeatedFixed32()[] = 56;
+        $m->getRepeatedFixed64()[] = 47;
+        $m->getRepeatedFixed64()[] = 57;
+        $m->getRepeatedSfixed32()[] = -46;
+        $m->getRepeatedSfixed32()[] = -56;
+        $m->getRepeatedSfixed64()[] = -47;
+        $m->getRepeatedSfixed64()[] = -57;
+        $m->getRepeatedFloat()[] = 1.5;
+        $m->getRepeatedFloat()[] = 2.5;
+        $m->getRepeatedDouble()[] = 1.6;
+        $m->getRepeatedDouble()[] = 2.6;
+        $m->getRepeatedBool()[] = true;
+        $m->getRepeatedBool()[] = false;
+        $m->getRepeatedEnum()[] = TestEnum::ONE;
+        $m->getRepeatedEnum()[] = TestEnum::ZERO;
+    }
+
+    public static function assertTestPackedMessage($m)
+    {
+        assert(2 === count($m->getRepeatedInt32()));
+        assert(2 === count($m->getRepeatedInt64()));
+        assert(2 === count($m->getRepeatedUint32()));
+        assert(2 === count($m->getRepeatedUint64()));
+        assert(2 === count($m->getRepeatedSint32()));
+        assert(2 === count($m->getRepeatedSint64()));
+        assert(2 === count($m->getRepeatedFixed32()));
+        assert(2 === count($m->getRepeatedFixed64()));
+        assert(2 === count($m->getRepeatedSfixed32()));
+        assert(2 === count($m->getRepeatedSfixed64()));
+        assert(2 === count($m->getRepeatedFloat()));
+        assert(2 === count($m->getRepeatedDouble()));
+        assert(2 === count($m->getRepeatedBool()));
+        assert(2 === count($m->getRepeatedEnum()));
+
+        assert(-42 === $m->getRepeatedInt32()[0]);
+        assert(-52 === $m->getRepeatedInt32()[1]);
+        assert(-43 === $m->getRepeatedInt64()[0]);
+        assert(-53 === $m->getRepeatedInt64()[1]);
+        assert(42  === $m->getRepeatedUint32()[0]);
+        assert(52  === $m->getRepeatedUint32()[1]);
+        assert(43  === $m->getRepeatedUint64()[0]);
+        assert(53  === $m->getRepeatedUint64()[1]);
+        assert(-44 === $m->getRepeatedSint32()[0]);
+        assert(-54 === $m->getRepeatedSint32()[1]);
+        assert(-45 === $m->getRepeatedSint64()[0]);
+        assert(-55 === $m->getRepeatedSint64()[1]);
+        assert(46  === $m->getRepeatedFixed32()[0]);
+        assert(56  === $m->getRepeatedFixed32()[1]);
+        assert(47  === $m->getRepeatedFixed64()[0]);
+        assert(57  === $m->getRepeatedFixed64()[1]);
+        assert(-46 === $m->getRepeatedSfixed32()[0]);
+        assert(-56 === $m->getRepeatedSfixed32()[1]);
+        assert(-47 === $m->getRepeatedSfixed64()[0]);
+        assert(-57 === $m->getRepeatedSfixed64()[1]);
+        assert(1.5 === $m->getRepeatedFloat()[0]);
+        assert(2.5 === $m->getRepeatedFloat()[1]);
+        assert(1.6 === $m->getRepeatedDouble()[0]);
+        assert(2.6 === $m->getRepeatedDouble()[1]);
+        assert(true  === $m->getRepeatedBool()[0]);
+        assert(false === $m->getRepeatedBool()[1]);
+        assert(TestEnum::ONE  === $m->getRepeatedEnum()[0]);
+        assert(TestEnum::ZERO === $m->getRepeatedEnum()[1]);
+    }
+
+    public static function getGoldenTestPackedMessage()
+    {
+        return hex2bin(
+            "D2050AD6FFFFFF0FCCFFFFFF0F" .
+            "DA0514D5FFFFFFFFFFFFFFFF01CBFFFFFFFFFFFFFFFF01" .
+            "E205022A34" .
+            "EA05022B35" .
+            "F20502576B" .
+            "FA0502596D" .
+            "8206082E00000038000000" .
+            "8A06102F000000000000003900000000000000" .
+            "920608D2FFFFFFC8FFFFFF" .
+            "9A0610D1FFFFFFFFFFFFFFC7FFFFFFFFFFFFFF" .
+            "A206080000C03F00002040" .
+            "AA06109A9999999999F93FCDCCCCCCCCCC0440" .
+            "B206020100" .
+            "BA06020100"
+        );
+    }
+
+    public static function getGoldenTestUnpackedMessage()
+    {
+        return hex2bin(
+            "D005D6FFFFFF0FD005CCFFFFFF0F" .
+            "D805D5FFFFFFFFFFFFFFFF01D805CBFFFFFFFFFFFFFFFF01" .
+            "E0052AE00534" .
+            "E8052BE80535" .
+            "F00557F0056B" .
+            "F80559F8056D" .
+            "85062E000000850638000000" .
+            "89062F0000000000000089063900000000000000" .
+            "9506D2FFFFFF9506C8FFFFFF" .
+            "9906D1FFFFFFFFFFFFFF9906C7FFFFFFFFFFFFFF" .
+            "A5060000C03FA50600002040" .
+            "A9069A9999999999F93FA906CDCCCCCCCCCC0440" .
+            "B00601B00600" .
+            "B80601B80600"
+        );
+    }
+}
diff --git a/phpunit.xml b/phpunit.xml
new file mode 100644
index 0000000..d311c50
--- /dev/null
+++ b/phpunit.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<phpunit bootstrap="./vendor/autoload.php"
+         colors="true">
+  <testsuites>
+    <testsuite name="protobuf-tests">
+      <file>php/tests/php_implementation_test.php</file>
+      <file>php/tests/array_test.php</file>
+      <file>php/tests/encode_decode_test.php</file>
+      <file>php/tests/generated_class_test.php</file>
+      <file>php/tests/map_field_test.php</file>
+    </testsuite>
+  </testsuites>
+</phpunit>
diff --git a/src/Makefile.am b/src/Makefile.am
index f70e955..13952e5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -162,6 +162,7 @@
   google/protobuf/compiler/js/js_generator.h                    \
   google/protobuf/compiler/objectivec/objectivec_generator.h    \
   google/protobuf/compiler/objectivec/objectivec_helpers.h      \
+  google/protobuf/compiler/php/php_generator.h                  \
   google/protobuf/compiler/python/python_generator.h            \
   google/protobuf/compiler/ruby/ruby_generator.h                \
   google/protobuf/util/type_resolver.h                          \
@@ -443,6 +444,7 @@
   google/protobuf/compiler/objectivec/objectivec_oneof.h       \
   google/protobuf/compiler/objectivec/objectivec_primitive_field.cc \
   google/protobuf/compiler/objectivec/objectivec_primitive_field.h \
+  google/protobuf/compiler/php/php_generator.cc                \
   google/protobuf/compiler/python/python_generator.cc          \
   google/protobuf/compiler/ruby/ruby_generator.cc              \
   google/protobuf/compiler/csharp/csharp_doc_comment.cc        \
diff --git a/src/google/protobuf/compiler/main.cc b/src/google/protobuf/compiler/main.cc
index 2f3c5b8..b83b8f3 100644
--- a/src/google/protobuf/compiler/main.cc
+++ b/src/google/protobuf/compiler/main.cc
@@ -35,8 +35,7 @@
 #include <google/protobuf/compiler/python/python_generator.h>
 #include <google/protobuf/compiler/java/java_generator.h>
 #include <google/protobuf/compiler/javanano/javanano_generator.h>
-// TODO(teboring): Add it back when php implementation is ready
-// #include <google/protobuf/compiler/php/php_generator.h>
+#include <google/protobuf/compiler/php/php_generator.h>
 #include <google/protobuf/compiler/ruby/ruby_generator.h>
 #include <google/protobuf/compiler/csharp/csharp_generator.h>
 #include <google/protobuf/compiler/objectivec/objectivec_generator.h>
@@ -68,11 +67,10 @@
   cli.RegisterGenerator("--javanano_out", &javanano_generator,
                         "Generate Java Nano source file.");
 
-  // TODO(teboring): Add it back when php implementation is ready
   // PHP
-  // google::protobuf::compiler::php::Generator php_generator;
-  // cli.RegisterGenerator("--php_out", &php_generator,
-  //                      "Generate PHP source file.");
+  google::protobuf::compiler::php::Generator php_generator;
+  cli.RegisterGenerator("--php_out", &php_generator,
+                        "Generate PHP source file.");
 
   // Ruby
   google::protobuf::compiler::ruby::Generator rb_generator;
diff --git a/src/google/protobuf/compiler/php/php_generator.cc b/src/google/protobuf/compiler/php/php_generator.cc
new file mode 100644
index 0000000..75ddb40
--- /dev/null
+++ b/src/google/protobuf/compiler/php/php_generator.cc
@@ -0,0 +1,781 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <google/protobuf/compiler/php/php_generator.h>
+
+#include <google/protobuf/compiler/code_generator.h>
+#include <google/protobuf/compiler/plugin.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/io/printer.h>
+#include <google/protobuf/io/zero_copy_stream.h>
+#include <google/protobuf/stubs/strutil.h>
+
+#include <sstream>
+
+using google::protobuf::internal::scoped_ptr;
+
+const std::string kDescriptorFile = "google/protobuf/descriptor.proto";
+const std::string kDescriptorPackageName = "Google\\Protobuf\\Internal";
+
+namespace google {
+namespace protobuf {
+namespace compiler {
+namespace php {
+
+// Forward decls.
+std::string PhpName(const std::string& full_name, bool is_descriptor);
+std::string DefaultForField(google::protobuf::FieldDescriptor* field);
+std::string IntToString(int32 value);
+std::string GeneratedFileName(const std::string& proto_file,
+                              bool is_descriptor);
+std::string LabelForField(google::protobuf::FieldDescriptor* field);
+std::string TypeName(google::protobuf::FieldDescriptor* field);
+std::string UnderscoresToCamelCase(const string& name, bool cap_first_letter);
+std::string EscapeDollor(const string& to_escape);
+std::string BinaryToHex(const string& binary);
+void GenerateMessage(const string& name_prefix,
+                     const google::protobuf::Descriptor* message,
+                     bool is_descriptor,
+                     google::protobuf::io::Printer* printer);
+void GenerateEnum(const google::protobuf::EnumDescriptor* en,
+                  google::protobuf::io::Printer* printer);
+void Indent(google::protobuf::io::Printer* printer);
+void Outdent(google::protobuf::io::Printer* printer);
+
+std::string MessageName(const google::protobuf::Descriptor* message,
+                        bool is_descriptor) {
+  string message_name = message->name();
+  const google::protobuf::Descriptor* descriptor = message->containing_type();
+  while (descriptor != NULL) {
+    message_name = descriptor->name() + '_' + message_name;
+    descriptor = descriptor->containing_type();
+  }
+  return PhpName(message->file()->package(), is_descriptor) + '\\' +
+         message_name;
+}
+
+std::string MessageFullName(const google::protobuf::Descriptor* message,
+                            bool is_descriptor) {
+  if (is_descriptor) {
+    return StringReplace(message->full_name(),
+                         "google.protobuf",
+                         "google.protobuf.internal", false);
+  } else {
+    return message->full_name();
+  }
+}
+
+std::string EnumFullName(const google::protobuf::EnumDescriptor* envm,
+                         bool is_descriptor) {
+  if (is_descriptor) {
+    return StringReplace(envm->full_name(),
+                         "google.protobuf",
+                         "google.protobuf.internal", false);
+  } else {
+    return envm->full_name();
+  }
+}
+
+std::string EnumClassName(const google::protobuf::EnumDescriptor* envm) {
+  string enum_class_name = envm->name();
+  const google::protobuf::Descriptor* descriptor = envm->containing_type();
+  while (descriptor != NULL) {
+    enum_class_name = descriptor->name() + '_' + enum_class_name;
+    descriptor = descriptor->containing_type();
+  }
+  return enum_class_name;
+}
+
+std::string EnumName(const google::protobuf::EnumDescriptor* envm,
+                     bool is_descriptor) {
+  string enum_name = EnumClassName(envm);
+  return PhpName(envm->file()->package(), is_descriptor) + '\\' + enum_name;
+}
+
+std::string PhpName(const std::string& full_name, bool is_descriptor) {
+  if (is_descriptor) {
+    return kDescriptorPackageName;
+  }
+
+  std::string result;
+  bool cap_next_letter = true;
+  for (int i = 0; i < full_name.size(); i++) {
+    if ('a' <= full_name[i] && full_name[i] <= 'z' && cap_next_letter) {
+      result += full_name[i] + ('A' - 'a');
+      cap_next_letter = false;
+    } else if (full_name[i] == '.') {
+      result += '\\';
+      cap_next_letter = true;
+    } else {
+      result += full_name[i];
+      cap_next_letter = false;
+    }
+  }
+  return result;
+}
+
+std::string DefaultForField(const google::protobuf::FieldDescriptor* field) {
+  switch (field->type()) {
+    case FieldDescriptor::TYPE_INT32:
+    case FieldDescriptor::TYPE_INT64:
+    case FieldDescriptor::TYPE_UINT32:
+    case FieldDescriptor::TYPE_UINT64:
+    case FieldDescriptor::TYPE_SINT32:
+    case FieldDescriptor::TYPE_SINT64:
+    case FieldDescriptor::TYPE_FIXED32:
+    case FieldDescriptor::TYPE_FIXED64:
+    case FieldDescriptor::TYPE_SFIXED32:
+    case FieldDescriptor::TYPE_SFIXED64:
+    case FieldDescriptor::TYPE_ENUM: return "0";
+    case FieldDescriptor::TYPE_DOUBLE:
+    case FieldDescriptor::TYPE_FLOAT: return "0.0";
+    case FieldDescriptor::TYPE_BOOL: return "false";
+    case FieldDescriptor::TYPE_STRING:
+    case FieldDescriptor::TYPE_BYTES: return "''";
+    case FieldDescriptor::TYPE_MESSAGE:
+    case FieldDescriptor::TYPE_GROUP: return "null";
+    default: assert(false); return "";
+  }
+}
+
+std::string GeneratedFileName(const std::string& proto_file,
+                              bool is_descriptor) {
+  if (is_descriptor) {
+    return "descriptor_internal.pb.php";
+  } else {
+    int lastindex = proto_file.find_last_of(".");
+    return proto_file.substr(0, lastindex) + ".pb.php";
+  }
+}
+
+std::string IntToString(int32 value) {
+  std::ostringstream os;
+  os << value;
+  return os.str();
+}
+
+std::string LabelForField(const google::protobuf::FieldDescriptor* field) {
+  switch (field->label()) {
+    case FieldDescriptor::LABEL_OPTIONAL: return "optional";
+    case FieldDescriptor::LABEL_REQUIRED: return "required";
+    case FieldDescriptor::LABEL_REPEATED: return "repeated";
+    default: assert(false); return "";
+  }
+}
+
+std::string TypeName(const google::protobuf::FieldDescriptor* field) {
+  switch (field->type()) {
+    case FieldDescriptor::TYPE_INT32: return "int32";
+    case FieldDescriptor::TYPE_INT64: return "int64";
+    case FieldDescriptor::TYPE_UINT32: return "uint32";
+    case FieldDescriptor::TYPE_UINT64: return "uint64";
+    case FieldDescriptor::TYPE_SINT32: return "sint32";
+    case FieldDescriptor::TYPE_SINT64: return "sint64";
+    case FieldDescriptor::TYPE_FIXED32: return "fixed32";
+    case FieldDescriptor::TYPE_FIXED64: return "fixed64";
+    case FieldDescriptor::TYPE_SFIXED32: return "sfixed32";
+    case FieldDescriptor::TYPE_SFIXED64: return "sfixed64";
+    case FieldDescriptor::TYPE_DOUBLE: return "double";
+    case FieldDescriptor::TYPE_FLOAT: return "float";
+    case FieldDescriptor::TYPE_BOOL: return "bool";
+    case FieldDescriptor::TYPE_ENUM: return "enum";
+    case FieldDescriptor::TYPE_STRING: return "string";
+    case FieldDescriptor::TYPE_BYTES: return "bytes";
+    case FieldDescriptor::TYPE_MESSAGE: return "message";
+    case FieldDescriptor::TYPE_GROUP: return "group";
+    default: assert(false); return "";
+  }
+}
+
+std::string EnumOrMessageSuffix(
+    const google::protobuf::FieldDescriptor* field, bool is_descriptor) {
+  if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+    return ", '" + MessageFullName(field->message_type(), is_descriptor) + "'";
+  }
+  if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
+    return ", '" + EnumFullName(field->enum_type(), is_descriptor) + "'";
+  }
+  return "";
+}
+
+// Converts a name to camel-case. If cap_first_letter is true, capitalize the
+// first letter.
+std::string UnderscoresToCamelCase(const string& input, bool cap_first_letter) {
+  std::string result;
+  for (int i = 0; i < input.size(); i++) {
+    if ('a' <= input[i] && input[i] <= 'z') {
+      if (cap_first_letter) {
+        result += input[i] + ('A' - 'a');
+      } else {
+        result += input[i];
+      }
+      cap_first_letter = false;
+    } else if ('A' <= input[i] && input[i] <= 'Z') {
+      if (i == 0 && !cap_first_letter) {
+        // Force first letter to lower-case unless explicitly told to
+        // capitalize it.
+        result += input[i] + ('a' - 'A');
+      } else {
+        // Capital letters after the first are left as-is.
+        result += input[i];
+      }
+      cap_first_letter = false;
+    } else if ('0' <= input[i] && input[i] <= '9') {
+      result += input[i];
+      cap_first_letter = true;
+    } else {
+      cap_first_letter = true;
+    }
+  }
+  // Add a trailing "_" if the name should be altered.
+  if (input[input.size() - 1] == '#') {
+    result += '_';
+  }
+  return result;
+}
+
+std::string EscapeDollor(const string& to_escape) {
+  return StringReplace(to_escape, "$", "\\$", true);
+}
+
+std::string BinaryToHex(const string& src) {
+  string dest;
+  size_t i;
+  unsigned char symbol[16] = {
+    '0', '1', '2', '3',
+    '4', '5', '6', '7',
+    '8', '9', 'a', 'b',
+    'c', 'd', 'e', 'f',
+  };
+
+  dest.resize(src.size() * 2);
+  char* append_ptr = &dest[0];
+
+  for (i = 0; i < src.size(); i++) {
+    *append_ptr++ = symbol[(src[i] & 0xf0) >> 4];
+    *append_ptr++ = symbol[src[i] & 0x0f];
+  }
+
+  return dest;
+}
+
+void Indent(google::protobuf::io::Printer* printer) {
+  printer->Indent();
+  printer->Indent();
+}
+void Outdent(google::protobuf::io::Printer* printer) {
+  printer->Outdent();
+  printer->Outdent();
+}
+
+void GenerateField(const google::protobuf::FieldDescriptor* field,
+                   google::protobuf::io::Printer* printer, bool is_descriptor) {
+  if (field->is_repeated()) {
+    printer->Print(
+        "private $@name@;\n",
+        "name", field->name());
+  } else if (field->containing_oneof()) {
+    // Oneof fields are handled by GenerateOneofField.
+    return;
+  } else {
+    printer->Print(
+        "private $@name@ = @default@;\n",
+        "name", field->name(),
+        "default", DefaultForField(field));
+  }
+
+  if (is_descriptor) {
+    printer->Print(
+        "private $has_@name@ = false;\n",
+        "name", field->name());
+  }
+}
+
+void GenerateOneofField(const google::protobuf::OneofDescriptor* oneof,
+                        google::protobuf::io::Printer* printer) {
+  // Oneof property needs to be protected in order to be accessed by parent
+  // class in implementation.
+  printer->Print(
+      "protected $@name@;\n",
+      "name", oneof->name());
+}
+
+void GenerateFieldAccessor(const google::protobuf::FieldDescriptor* field,
+                           bool is_descriptor,
+                           google::protobuf::io::Printer* printer) {
+  const OneofDescriptor* oneof = field->containing_oneof();
+
+  // Generate getter.
+  if (oneof != NULL) {
+    printer->Print(
+        "public function get@camel_name@()\n"
+        "{\n"
+        "    return $this->readOneof(@number@);\n"
+        "}\n\n",
+        "camel_name", UnderscoresToCamelCase(field->name(), true),
+        "number", IntToString(field->number()));
+  } else {
+    printer->Print(
+        "public function get@camel_name@()\n"
+        "{\n"
+        "    return $this->@name@;\n"
+        "}\n\n",
+        "camel_name", UnderscoresToCamelCase(field->name(), true), "name",
+        field->name());
+  }
+
+  // Generate setter.
+  printer->Print(
+      "public function set@camel_name@(@var@)\n"
+      "{\n",
+      "camel_name", UnderscoresToCamelCase(field->name(), true),
+      "var", (field->is_repeated() ||
+              field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) ?
+             "&$var": "$var");
+
+  Indent(printer);
+
+  // Type check.
+  if (field->is_map()) {
+  } else if (field->is_repeated()) {
+    printer->Print(
+        "GPBUtil::checkRepeatedField($var, GPBType::@type@",
+        "type", ToUpper(field->type_name()));
+    if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+      printer->Print(
+          ", \\@class_name@);\n",
+          "class_name",
+          MessageName(field->message_type(), is_descriptor) + "::class");
+    } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
+      printer->Print(
+          ", @class_name@);\n",
+          "class_name",
+          EnumName(field->enum_type(), is_descriptor) + "::class");
+    } else {
+      printer->Print(");\n");
+    }
+  } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+    printer->Print(
+        "GPBUtil::checkMessage($var, \\@class_name@::class);\n",
+        "class_name", MessageName(field->message_type(), is_descriptor));
+  } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
+    printer->Print(
+        "GPBUtil::checkEnum($var, \\@class_name@::class);\n",
+        "class_name", EnumName(field->enum_type(), is_descriptor));
+  } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_STRING) {
+    printer->Print(
+        "GPBUtil::checkString($var, @utf8@);\n",
+        "utf8",
+        field->type() == FieldDescriptor::TYPE_STRING ? "True": "False");
+  } else {
+    printer->Print(
+        "GPBUtil::check@type@($var);\n",
+        "type", UnderscoresToCamelCase(field->cpp_type_name(), true));
+  }
+
+  if (oneof != NULL) {
+    printer->Print(
+        "$this->writeOneof(@number@, $var);\n",
+        "number", IntToString(field->number()));
+  } else {
+    printer->Print(
+        "$this->@name@ = $var;\n",
+        "name", field->name());
+  }
+
+  // Set has bit for proto2 only.
+  if (is_descriptor) {
+    printer->Print(
+        "$this->has_@field_name@ = true;\n",
+        "field_name", field->name());
+  }
+
+  Outdent(printer);
+
+  printer->Print(
+      "}\n\n");
+
+  // Generate has method for proto2 only.
+  if (is_descriptor) {
+    printer->Print(
+        "public function has@camel_name@()\n"
+        "{\n"
+        "    return $this->has_@field_name@;\n"
+        "}\n\n",
+        "camel_name", UnderscoresToCamelCase(field->name(), true),
+        "field_name", field->name());
+  }
+}
+
+void GenerateRepeatedFieldDecode(
+    const google::protobuf::FieldDescriptor* field,
+    google::protobuf::io::Printer* printer) {
+  printer->Print(
+      "if ($input->read@cap_wire_type@($var)) return False;\n"
+      "$this->get@cap_field_name@() []= $var;\n",
+      "cap_field_name", UnderscoresToCamelCase(field->name(), true),
+      "cap_wire_type", UnderscoresToCamelCase(field->type_name(), true));
+}
+
+void GeneratePrimitiveFieldDecode(
+    const google::protobuf::FieldDescriptor* field,
+    google::protobuf::io::Printer* printer) {
+  printer->Print(
+      "if ($input->read@cap_wire_type@($var)) return False;\n"
+      "$this->set@cap_field_name@($var);\n",
+      "cap_field_name", UnderscoresToCamelCase(field->name(), true),
+      "cap_wire_type", UnderscoresToCamelCase(field->type_name(), true));
+}
+
+void GenerateFieldDecode(const google::protobuf::FieldDescriptor* field,
+                         google::protobuf::io::Printer* printer) {
+  printer->Print(
+      "case @number@:\n",
+      "number", IntToString(field->number()));
+  Indent(printer);
+
+  if (field->is_repeated()) {
+    GenerateRepeatedFieldDecode(field, printer);
+  } else {
+    GeneratePrimitiveFieldDecode(field, printer);
+  }
+
+  printer->Print(
+      "break;\n");
+  Outdent(printer);
+}
+
+void GenerateMessage(const string& name_prefix,
+                     const google::protobuf::Descriptor* message,
+                     bool is_descriptor,
+                     google::protobuf::io::Printer* printer) {
+  // Don't generate MapEntry messages -- we use the PHP extension's native
+  // support for map fields instead.
+  if (message->options().map_entry()) {
+    return;
+  }
+
+  string message_name = name_prefix.empty()?
+      message->name() : name_prefix + "_" + message->name();
+
+  printer->Print(
+      "class @name@ extends \\Google\\Protobuf\\Internal\\Message\n"
+      "{\n",
+      "name", message_name);
+  Indent(printer);
+
+  // Field and oneof definitions.
+  for (int i = 0; i < message->field_count(); i++) {
+    const FieldDescriptor* field = message->field(i);
+    GenerateField(field, printer, is_descriptor);
+  }
+  for (int i = 0; i < message->oneof_decl_count(); i++) {
+    const OneofDescriptor* oneof = message->oneof_decl(i);
+    GenerateOneofField(oneof, printer);
+  }
+  printer->Print("\n");
+
+  // Field and oneof accessors.
+  for (int i = 0; i < message->field_count(); i++) {
+    const FieldDescriptor* field = message->field(i);
+    GenerateFieldAccessor(field, is_descriptor, printer);
+  }
+  for (int i = 0; i < message->oneof_decl_count(); i++) {
+    const google::protobuf::OneofDescriptor* oneof = message->oneof_decl(i);
+    printer->Print(
+      "public function get@camel_name@()\n"
+      "{\n"
+      "    return $this->@name@;\n"
+      "}\n\n",
+      "camel_name", UnderscoresToCamelCase(oneof->name(), true), "name",
+      oneof->name());
+  }
+
+  Outdent(printer);
+  printer->Print("}\n\n");
+
+  // Nested messages and enums.
+  for (int i = 0; i < message->nested_type_count(); i++) {
+    GenerateMessage(message_name, message->nested_type(i), is_descriptor,
+                    printer);
+  }
+  for (int i = 0; i < message->enum_type_count(); i++) {
+    GenerateEnum(message->enum_type(i), printer);
+  }
+}
+
+void GenerateEnumToPool(const google::protobuf::EnumDescriptor* en,
+                        bool is_descriptor,
+                        google::protobuf::io::Printer* printer) {
+  printer->Print(
+      "$pool->addEnum('@name@', @class_name@::class)\n",
+      "name", EnumFullName(en, is_descriptor),
+      "class_name", en->name());
+  Indent(printer);
+
+  for (int i = 0; i < en->value_count(); i++) {
+    const EnumValueDescriptor* value = en->value(i);
+    printer->Print(
+        "->value(\"@name@\", @number@)\n",
+        "name", value->name(),
+        "number", IntToString(value->number()));
+  }
+  printer->Print("->finalizeToPool();\n\n");
+  Outdent(printer);
+}
+
+void GenerateMessageToPool(const string& name_prefix,
+                           const google::protobuf::Descriptor* message,
+                           bool is_descriptor,
+                           google::protobuf::io::Printer* printer) {
+  // Don't generate MapEntry messages -- we use the PHP extension's native
+  // support for map fields instead.
+  if (message->options().map_entry()) {
+    return;
+  }
+  string class_name = name_prefix.empty()?
+      message->name() : name_prefix + "_" + message->name();
+
+  printer->Print(
+      "$pool->addMessage('@message@', @class_name@::class)\n",
+      "message", MessageFullName(message, is_descriptor),
+      "class_name", class_name);
+
+  Indent(printer);
+
+  for (int i = 0; i < message->field_count(); i++) {
+    const FieldDescriptor* field = message->field(i);
+    if (field->is_map()) {
+      const FieldDescriptor* key =
+          field->message_type()->FindFieldByName("key");
+      const FieldDescriptor* val =
+          field->message_type()->FindFieldByName("value");
+      printer->Print(
+          "->map('@field@', GPBType::@key@, "
+          "GPBType::@value@, @number@@other@)\n",
+          "field", field->name(),
+          "key", ToUpper(key->type_name()),
+          "value", ToUpper(val->type_name()),
+          "number", SimpleItoa(field->number()),
+          "other", EnumOrMessageSuffix(val, is_descriptor));
+    } else if (!field->containing_oneof()) {
+      printer->Print(
+          "->@label@('@field@', GPBType::@type@, @number@@other@)\n",
+          "field", field->name(),
+          "label", LabelForField(field),
+          "type", ToUpper(field->type_name()),
+          "number", SimpleItoa(field->number()),
+          "other", EnumOrMessageSuffix(field, is_descriptor));
+    }
+  }
+
+  // oneofs.
+  for (int i = 0; i < message->oneof_decl_count(); i++) {
+    const OneofDescriptor* oneof = message->oneof_decl(i);
+    printer->Print("->oneof(@name@)\n",
+                   "name", oneof->name());
+    Indent(printer);
+    for (int index = 0; index < oneof->field_count(); index++) {
+      const FieldDescriptor* field = oneof->field(index);
+      printer->Print(
+          "->value('@field@', GPBType::@type@, @number@@other@)\n",
+          "field", field->name(),
+          "type", ToUpper(field->type_name()),
+          "number", SimpleItoa(field->number()),
+          "other", EnumOrMessageSuffix(field, is_descriptor));
+    }
+    printer->Print("->finish()\n");
+    Outdent(printer);
+  }
+
+  printer->Print(
+      "->finalizeToPool();\n");
+
+  Outdent(printer);
+
+  printer->Print(
+      "\n");
+
+  for (int i = 0; i < message->nested_type_count(); i++) {
+    GenerateMessageToPool(class_name, message->nested_type(i), is_descriptor,
+                          printer);
+  }
+  for (int i = 0; i < message->enum_type_count(); i++) {
+    GenerateEnumToPool(message->enum_type(i), is_descriptor, printer);
+  }
+}
+
+void GenerateAddFileToPool(const google::protobuf::FileDescriptor* file,
+                           bool is_descriptor,
+                           google::protobuf::io::Printer* printer) {
+  if (is_descriptor) {
+    printer->Print("$pool = DescriptorPool::getGeneratedPool();\n\n");
+
+    for (int i = 0; i < file->message_type_count(); i++) {
+      GenerateMessageToPool("", file->message_type(i), is_descriptor, printer);
+    }
+    for (int i = 0; i < file->enum_type_count(); i++) {
+      GenerateEnumToPool(file->enum_type(i), is_descriptor, printer);
+    }
+
+    printer->Print(
+        "$pool->finish();\n");
+  } else {
+    // Add messages and enums to descriptor pool.
+    printer->Print("$pool = DescriptorPool::getGeneratedPool();\n\n");
+
+    FileDescriptorSet files;
+    FileDescriptorProto* file_proto = files.add_file();
+    file->CopyTo(file_proto);
+    string files_data;
+    files.SerializeToString(&files_data);
+
+    printer->Print("$pool->internalAddGeneratedFile(hex2bin(\n");
+    Indent(printer);
+
+    // Only write 30 bytes per line.
+    static const int kBytesPerLine = 30;
+    for (int i = 0; i < files_data.size(); i += kBytesPerLine) {
+      printer->Print(
+          "\"@data@\"@dot@\n",
+          "data", BinaryToHex(files_data.substr(i, kBytesPerLine)),
+          "dot", i + kBytesPerLine < files_data.size() ? " ." : "");
+    }
+
+    Outdent(printer);
+    printer->Print(
+        "));\n\n");
+  }
+
+}
+
+void GenerateEnum(const google::protobuf::EnumDescriptor* en,
+                  google::protobuf::io::Printer* printer) {
+  printer->Print(
+      "class @name@\n"
+      "{\n",
+      "name", EnumClassName(en));
+  Indent(printer);
+
+  for (int i = 0; i < en->value_count(); i++) {
+    const EnumValueDescriptor* value = en->value(i);
+    printer->Print("const @name@ = @number@;\n",
+                   "name", value->name(),
+                   "number", IntToString(value->number()));
+  }
+  Outdent(printer);
+  printer->Print("}\n\n");
+}
+
+void GenerateUseDeclaration(bool is_descriptor,
+                            google::protobuf::io::Printer* printer) {
+  if (!is_descriptor) {
+    printer->Print(
+        "use Google\\Protobuf\\Internal\\DescriptorPool;\n"
+        "use Google\\Protobuf\\Internal\\GPBType;\n"
+        "use Google\\Protobuf\\Internal\\RepeatedField;\n"
+        "use Google\\Protobuf\\Internal\\GPBUtil;\n\n");
+  } else {
+    printer->Print(
+        "use Google\\Protobuf\\Internal\\DescriptorPool;\n"
+        "use Google\\Protobuf\\Internal\\GPBType;\n"
+        "use Google\\Protobuf\\Internal\\GPBWire;\n"
+        "use Google\\Protobuf\\Internal\\RepeatedField;\n"
+        "use Google\\Protobuf\\Internal\\InputStream;\n\n"
+        "use Google\\Protobuf\\Internal\\GPBUtil;\n\n");
+  }
+}
+
+void GenerateFile(const google::protobuf::FileDescriptor* file,
+                  bool is_descriptor, google::protobuf::io::Printer* printer) {
+  printer->Print(
+    "<?php\n"
+    "# Generated by the protocol buffer compiler.  DO NOT EDIT!\n"
+    "# source: @filename@\n"
+    "\n",
+    "filename", file->name());
+  if (!file->package().empty()) {
+    printer->Print("namespace @name@;\n\n",
+                   "name", PhpName(file->package(), is_descriptor));
+  }
+
+  for (int i = 0; i < file->dependency_count(); i++) {
+    const std::string& name = file->dependency(i)->name();
+    printer->Print("require_once('@name@');\n", "name",
+                   GeneratedFileName(name, is_descriptor));
+  }
+
+  GenerateUseDeclaration(is_descriptor, printer);
+
+  for (int i = 0; i < file->message_type_count(); i++) {
+    GenerateMessage("", file->message_type(i), is_descriptor, printer);
+  }
+  for (int i = 0; i < file->enum_type_count(); i++) {
+    GenerateEnum(file->enum_type(i), printer);
+  }
+
+  GenerateAddFileToPool(file, is_descriptor, printer);
+}
+
+bool Generator::Generate(
+    const FileDescriptor* file,
+    const string& parameter,
+    GeneratorContext* generator_context,
+    string* error) const {
+  bool is_descriptor = parameter == "internal";
+
+  if (is_descriptor && file->name() != kDescriptorFile) {
+    *error =
+        "Can only generate PHP code for google/protobuf/descriptor.proto.\n";
+    return false;
+  }
+
+  if (!is_descriptor && file->syntax() != FileDescriptor::SYNTAX_PROTO3) {
+    *error =
+        "Can only generate PHP code for proto3 .proto files.\n"
+        "Please add 'syntax = \"proto3\";' to the top of your .proto file.\n";
+    return false;
+  }
+
+  std::string filename = GeneratedFileName(file->name(), is_descriptor);
+  scoped_ptr<io::ZeroCopyOutputStream> output(
+      generator_context->Open(filename));
+  io::Printer printer(output.get(), '@');
+
+  GenerateFile(file, is_descriptor, &printer);
+
+  return true;
+}
+
+}  // namespace php
+}  // namespace compiler
+}  // namespace protobuf
+}  // namespace google
diff --git a/src/google/protobuf/compiler/php/php_generator.h b/src/google/protobuf/compiler/php/php_generator.h
new file mode 100644
index 0000000..ce2b000
--- /dev/null
+++ b/src/google/protobuf/compiler/php/php_generator.h
@@ -0,0 +1,57 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef GOOGLE_PROTOBUF_COMPILER_PHP_GENERATOR_H__
+#define GOOGLE_PROTOBUF_COMPILER_PHP_GENERATOR_H__
+
+#include <google/protobuf/compiler/code_generator.h>
+
+#include <string>
+
+namespace google {
+namespace protobuf {
+namespace compiler {
+namespace php {
+
+class LIBPROTOC_EXPORT Generator
+    : public google::protobuf::compiler::CodeGenerator {
+  virtual bool Generate(
+      const FileDescriptor* file,
+      const string& parameter,
+      GeneratorContext* generator_context,
+      string* error) const;
+};
+
+}  // namespace php
+}  // namespace compiler
+}  // namespace protobuf
+}  // namespace google
+
+#endif  // GOOGLE_PROTOBUF_COMPILER_PHP_GENERATOR_H__