Merge pull request #1259 from silviulica/master

Add a modified patch from craigcitro@ to handle namespace sharing.
diff --git a/csharp/src/Google.Protobuf.sln b/csharp/src/Google.Protobuf.sln
index 69ce9a4..72d87f7 100644
--- a/csharp/src/Google.Protobuf.sln
+++ b/csharp/src/Google.Protobuf.sln
@@ -1,7 +1,7 @@
 Microsoft Visual Studio Solution File, Format Version 12.00

-# Visual Studio 2013

-VisualStudioVersion = 12.0.31101.0

-MinimumVisualStudioVersion = 10.0.40219.1

+# Visual Studio 2015

+VisualStudioVersion = 14.0.24720.0

+MinimumVisualStudioVersion = 14.0.24720.0

 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.Protobuf", "Google.Protobuf\Google.Protobuf.csproj", "{6908BDCE-D925-43F3-94AC-A531E6DF2591}"

 EndProject

 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.Protobuf.Test", "Google.Protobuf.Test\Google.Protobuf.Test.csproj", "{DD01ED24-3750-4567-9A23-1DB676A15610}"

diff --git a/js/README.md b/js/README.md
index fc144a3..15d48c8 100644
--- a/js/README.md
+++ b/js/README.md
@@ -1,14 +1,159 @@
-This directory contains Protocol Buffer support for JavaScript.  This code works
-in browsers and in Node.js.
+Protocol Buffers - Google's data interchange format
+===================================================
 
-The packaging work for this is still in-progress.  For now you can just run the
-tests.  First you need to build the main C++ distribution because the code
-generator for JavaScript is written in C++:
+[![Build Status](https://travis-ci.org/google/protobuf.svg?branch=master)](https://travis-ci.org/google/protobuf)
 
-   $ ./autogen.sh
-   $ ./configure
-   $ make
+Copyright 2008 Google Inc.
 
-Then you can run the JavaScript tests in this directory:
+This directory contains the JavaScript Protocol Buffers runtime library.
 
-   $ cd js && gulp test
+The library is currently compatible with:
+
+1. CommonJS-style imports (eg. `var protos = require('my-protos');`)
+2. Closure-style imports (eg. `goog.require('my.package.MyProto');`)
+
+Support for ES6-style imports is not implemented yet.  Browsers can
+be supported by using Browserify, webpack, Closure Compiler, etc. to
+resolve imports at compile time.
+
+To use Protocol Buffers with JavaScript, you need two main components:
+
+1. The protobuf runtime library.  You can install this with
+   `npm install google-protobuf`, or use the files in this directory.
+2. The Protocol Compiler `protoc`.  This translates `.proto` files
+   into `.js` files.  The compiler is not currently available via
+   npm, but you can download a pre-built binary
+   [on GitHub](https://github.com/google/protobuf/releases)
+   (look for the `protoc-*.zip` files under **Downloads**).
+
+
+Setup
+=====
+
+First, obtain the Protocol Compiler.  The easiest way is to download
+a pre-built binary from [https://github.com/google/protobuf/releases](https://github.com/google/protobuf/releases).
+
+If you want, you can compile `protoc` from source instead.  To do this
+follow the instructions in [the top-level
+README](https://github.com/google/protobuf/blob/master/src/README.md).
+
+Once you have `protoc` compiled, you can run the tests by typing:
+
+    $ cd js
+    $ npm install
+    $ npm test
+
+    # If your protoc is somewhere else than ../src/protoc, instead do this.
+    # But make sure your protoc is the same version as this (or compatible)!
+    $ PROTOC=/usr/local/bin/protoc npm test
+
+This will run two separate copies of the tests: one that uses
+Closure Compiler style imports and one that uses CommonJS imports.
+You can see all the CommonJS files in `commonjs_out/`.
+If all of these tests pass, you know you have a working setup.
+
+
+Using Protocol Buffers in your own project
+==========================================
+
+To use Protocol Buffers in your own project, you need to integrate
+the Protocol Compiler into your build system.  The details are a
+little different depending on whether you are using Closure imports
+or CommonJS imports:
+
+Closure Imports
+---------------
+
+If you want to use Closure imports, your build should run a command
+like this:
+
+    $ protoc --js_out=library=myproto_libs,binary:. messages.proto base.proto
+
+For Closure imports, `protoc` will generate a single output file
+(`myproto_libs.js` in this example).  The generated file will `goog.provide()`
+all of the types defined in your .proto files.  For example, for the unit
+tests the generated files contain many `goog.provide` statements like:
+
+    goog.provide('proto.google.protobuf.DescriptorProto');
+    goog.provide('proto.google.protobuf.DescriptorProto.ExtensionRange');
+    goog.provide('proto.google.protobuf.DescriptorProto.ReservedRange');
+    goog.provide('proto.google.protobuf.EnumDescriptorProto');
+    goog.provide('proto.google.protobuf.EnumOptions');
+
+The generated code will also `goog.require()` many types in the core library,
+and they will require many types in the Google Closure library.  So make sure
+that your `goog.provide()` / `goog.require()` setup can find all of your
+generated code, the core library `.js` files in this directory, and the
+Google Closure library itself.
+
+Once you've done this, you should be able to import your types with
+statements like:
+
+    goog.require('proto.my.package.MyMessage');
+
+    var message = proto.my.package.MyMessage();
+
+CommonJS imports
+----------------
+
+If you want to use CommonJS imports, your build should run a command
+like this:
+
+    $ protoc --js_out=import_style=commonjs,binary:. messages.proto base.proto
+
+For CommonJS imports, `protoc` will spit out one file per input file
+(so `messages_pb.js` and `base_pb.js` in this example).  The generated
+code will depend on the core runtime, which should be in a file called
+`google-protobuf.js`.  If you are installing from `npm`, this file should
+already be built and available.  If you are running from GitHub, you need
+to build it first by running:
+
+    $ gulp dist
+
+Once you've done this, you should be able to import your types with
+statements like:
+
+    var messages = require('./messages_pb');
+
+    var message = new messages.MyMessage();
+
+The `--js_out` flag
+-------------------
+
+The syntax of the `--js_out` flag is:
+
+    --js_out=[OPTIONS:]output_dir
+
+Where `OPTIONS` are separated by commas.  Options are either `opt=val` or
+just `opt` (for options that don't take a value).  The available options
+are specified and documented in the `GeneratorOptions` struct in
+[src/google/protobuf/compiler/js/js_generator.h](https://github.com/google/protobuf/blob/master/src/google/protobuf/compiler/js/js_generator.h#L53).
+
+Some examples:
+
+- `--js_out=library=myprotos_lib.js,binary:.`: this contains the options
+  `library=myprotos.lib.js` and `binary` and outputs to the current directory.
+  The `import_style` option is left to the default, which is `closure`.
+- `--js_out=import_style=commonjs,binary:protos`: this contains the options
+  `import_style=commonjs` and `binary` and outputs to the directory `protos`.
+
+API
+===
+
+The API is not well-documented yet.  Here is a quick example to give you an
+idea of how the library generally works:
+
+    var message = new MyMessage();
+
+    message.setName("John Doe");
+    message.setAge(25);
+    message.setPhoneNumbers(["800-555-1212", "800-555-0000"]);
+
+    // Serializes to a UInt8Array.
+    bytes = message.serializeBinary();
+
+    var message2 = new MyMessage();
+    message2.deserializeBinary(bytes);
+
+For more examples, see the tests.  You can also look at the generated code
+to see what methods are defined for your generated messages.
diff --git a/js/binary/proto_test.js b/js/binary/proto_test.js
index 1cb7ff0..817f8a7 100644
--- a/js/binary/proto_test.js
+++ b/js/binary/proto_test.js
@@ -31,6 +31,8 @@
 // Test suite is written using Jasmine -- see http://jasmine.github.io/
 
 goog.require('goog.testing.asserts');
+
+// CommonJS-LoadFromFile: testbinary_pb proto.jspb.test
 goog.require('proto.jspb.test.ExtendsWithMessage');
 goog.require('proto.jspb.test.ForeignEnum');
 goog.require('proto.jspb.test.ForeignMessage');
diff --git a/js/commonjs/export.js b/js/commonjs/export.js
new file mode 100644
index 0000000..a3cfbd6
--- /dev/null
+++ b/js/commonjs/export.js
@@ -0,0 +1,22 @@
+/**
+ * @fileoverview Export symbols needed by generated code in CommonJS style.
+ *
+ * This effectively is our canonical list of what we publicly export from
+ * the google-protobuf.js file that we build at distribution time.
+ */
+
+goog.require('goog.object');
+goog.require('jspb.BinaryReader');
+goog.require('jspb.BinaryWriter');
+goog.require('jspb.ExtensionFieldInfo');
+goog.require('jspb.Message');
+
+exports.Message = jspb.Message;
+exports.BinaryReader = jspb.BinaryReader;
+exports.BinaryWriter = jspb.BinaryWriter;
+exports.ExtensionFieldInfo = jspb.ExtensionFieldInfo;
+
+// These are used by generated code but should not be used directly by clients.
+exports.exportSymbol = goog.exportSymbol;
+exports.inherits = goog.inherits;
+exports.object = {extend: goog.object.extend};
diff --git a/js/commonjs/export_asserts.js b/js/commonjs/export_asserts.js
new file mode 100644
index 0000000..5219d12
--- /dev/null
+++ b/js/commonjs/export_asserts.js
@@ -0,0 +1,37 @@
+/**
+ * @fileoverview Exports symbols needed only by tests.
+ *
+ * This file exports several Closure Library symbols that are only
+ * used by tests.  It is used to generate a file
+ * closure_asserts_commonjs.js that is only used at testing time.
+ */
+
+goog.require('goog.testing.asserts');
+
+var global = Function('return this')();
+
+// All of the closure "assert" functions are exported at the global level.
+//
+// The Google Closure assert functions start with assert, eg.
+//   assertThrows
+//   assertNotThrows
+//   assertTrue
+//   ...
+//
+// The one exception is the "fail" function.
+function shouldExport(str) {
+  return str.lastIndexOf('assert') === 0 || str == 'fail';
+}
+
+for (var key in global) {
+  if ((typeof key == "string") && global.hasOwnProperty(key) &&
+      shouldExport(key)) {
+    exports[key] = global[key];
+  }
+}
+
+// The COMPILED variable is set by Closure compiler to "true" when it compiles
+// JavaScript, so in practice this is equivalent to "exports.COMPILED = true".
+// This will disable some debugging functionality in debug.js.  We could
+// investigate whether this can/should be enabled in CommonJS builds.
+exports.COMPILED = COMPILED
diff --git a/js/commonjs/jasmine.json b/js/commonjs/jasmine.json
new file mode 100644
index 0000000..666b8ed
--- /dev/null
+++ b/js/commonjs/jasmine.json
@@ -0,0 +1,9 @@
+{
+    "spec_dir": "",
+    "spec_files": [
+        "*_test.js",
+        "binary/proto_test.js"
+    ],
+    "helpers": [
+    ]
+}
diff --git a/js/commonjs/rewrite_tests_for_commonjs.js b/js/commonjs/rewrite_tests_for_commonjs.js
new file mode 100644
index 0000000..dc5effe
--- /dev/null
+++ b/js/commonjs/rewrite_tests_for_commonjs.js
@@ -0,0 +1,92 @@
+/**
+ * @fileoverview Utility to translate test files to CommonJS imports.
+ *
+ * This is a somewhat hacky tool designed to do one very specific thing.
+ * All of the test files in *_test.js are written with Closure-style
+ * imports (goog.require()).  This works great for running the tests
+ * against Closure-style generated code, but we also want to run the
+ * tests against CommonJS-style generated code without having to fork
+ * the tests.
+ *
+ * Closure-style imports import each individual type by name.  This is
+ * very different than CommonJS imports which are by file.  So we put
+ * special comments in these tests like:
+ *
+ * // CommonJS-LoadFromFile: test_pb
+ * goog.require('proto.jspb.test.CloneExtension');
+ * goog.require('proto.jspb.test.Complex');
+ * goog.require('proto.jspb.test.DefaultValues');
+ *
+ * This script parses that special comment and uses it to generate proper
+ * CommonJS require() statements so that the tests can run and pass using
+ * CommonJS imports.  The script will change the above statements into:
+ *
+ *   var test_pb = require('test_pb');
+ *   googleProtobuf.exportSymbol('proto.jspb.test.CloneExtension', test_pb.CloneExtension, global);
+ *   googleProtobuf.exportSymbol('proto.jspb.test.Complex', test_pb.Complex, global);
+ *   googleProtobuf.exportSymbol('proto.jspb.test.DefaultValues', test_pb.DefaultValues, global);
+ *
+ * (The "exportSymbol" function will define the given names in the global
+ * namespace, taking care not to overwrite any previous value for
+ * "proto.jspb.test").
+ */
+
+var lineReader = require('readline').createInterface({
+  input: process.stdin,
+  output: process.stdout
+});
+
+function tryStripPrefix(str, prefix) {
+  if (str.lastIndexOf(prefix) !== 0) {
+    throw "String: " + str + " didn't start with: " + prefix;
+  }
+  return str.substr(prefix.length);
+}
+
+function camelCase(str) {
+  var ret = '';
+  var ucaseNext = false;
+  for (var i = 0; i < str.length; i++) {
+    if (str[i] == '-') {
+      ucaseNext = true;
+    } else if (ucaseNext) {
+      ret += str[i].toUpperCase();
+      ucaseNext = false;
+    } else {
+      ret += str[i];
+    }
+  }
+  return ret;
+}
+
+var module = null;
+var pkg = null;
+lineReader.on('line', function(line) {
+  var isRequire = line.match(/goog\.require\('([^']*)'\)/);
+  var isLoadFromFile = line.match(/CommonJS-LoadFromFile: (\S*) (.*)/);
+  var isSetTestOnly = line.match(/goog.setTestOnly()/);
+  if (isRequire) {
+    if (module) {  // Skip goog.require() lines before the first directive.
+      var fullSym = isRequire[1];
+      var sym = tryStripPrefix(fullSym, pkg);
+      console.log("googleProtobuf.exportSymbol('" + fullSym + "', " + module + sym + ', global);');
+    }
+  } else if (isLoadFromFile) {
+    if (!module) {
+      console.log("var googleProtobuf = require('google-protobuf');");
+      console.log("var asserts = require('closure_asserts_commonjs');");
+      console.log("var global = Function('return this')();");
+      console.log("");
+      console.log("// Bring asserts into the global namespace.");
+      console.log("googleProtobuf.object.extend(global, asserts);");
+    }
+    module = camelCase(isLoadFromFile[1])
+    pkg = isLoadFromFile[2];
+
+    if (module != "googleProtobuf") {  // We unconditionally require this in the header.
+      console.log("var " + module + " = require('" + isLoadFromFile[1] + "');");
+    }
+  } else if (!isSetTestOnly) {  // Remove goog.setTestOnly() lines.
+    console.log(line);
+  }
+});
diff --git a/js/debug_test.js b/js/debug_test.js
index 615fc7c..d7bf376 100644
--- a/js/debug_test.js
+++ b/js/debug_test.js
@@ -31,13 +31,16 @@
 goog.setTestOnly();
 
 goog.require('goog.testing.asserts');
+
+// CommonJS-LoadFromFile: google-protobuf
 goog.require('jspb.debug');
+
+// CommonJS-LoadFromFile: test_pb
 goog.require('proto.jspb.test.HasExtensions');
 goog.require('proto.jspb.test.IsExtension');
 goog.require('proto.jspb.test.Simple1');
 
 
-
 describe('debugTest', function() {
   it('testSimple1', function() {
     if (COMPILED) {
diff --git a/js/gulpfile.js b/js/gulpfile.js
index 79095d6..b0faed0 100644
--- a/js/gulpfile.js
+++ b/js/gulpfile.js
@@ -1,25 +1,79 @@
 var gulp = require('gulp');
 var exec = require('child_process').exec;
+var glob = require('glob');
 
-gulp.task('genproto', function (cb) {
-  exec('../src/protoc --js_out=library=testproto_libs,binary:. -I ../src -I . *.proto ../src/google/protobuf/descriptor.proto',
+var protoc = process.env.PROTOC || '../src/protoc';
+
+gulp.task('genproto_closure', function (cb) {
+  exec(protoc + ' --js_out=library=testproto_libs,binary:. -I ../src -I . *.proto ../src/google/protobuf/descriptor.proto',
        function (err, stdout, stderr) {
     console.log(stdout);
     console.log(stderr);
     cb(err);
   });
-})
+});
 
-gulp.task('deps', ['genproto'], function (cb) {
+gulp.task('genproto_commonjs', function (cb) {
+  exec('mkdir -p commonjs_out && ' + protoc + ' --js_out=import_style=commonjs,binary:commonjs_out -I ../src -I . *.proto ../src/google/protobuf/descriptor.proto',
+       function (err, stdout, stderr) {
+    console.log(stdout);
+    console.log(stderr);
+    cb(err);
+  });
+});
+
+gulp.task('dist', function (cb) {
+  // TODO(haberman): minify this more aggressively.
+  // Will require proper externs/exports.
+  exec('./node_modules/google-closure-library/closure/bin/calcdeps.py -i message.js -i binary/reader.js -i binary/writer.js -i commonjs/export.js -p . -p node_modules/google-closure-library/closure -o compiled --compiler_jar node_modules/google-closure-compiler/compiler.jar > google-protobuf.js',
+       function (err, stdout, stderr) {
+    console.log(stdout);
+    console.log(stderr);
+    cb(err);
+  });
+});
+
+gulp.task('commonjs_asserts', function (cb) {
+  exec('mkdir -p commonjs_out && ./node_modules/google-closure-library/closure/bin/calcdeps.py -i commonjs/export_asserts.js -p . -p node_modules/google-closure-library/closure -o compiled --compiler_jar node_modules/google-closure-compiler/compiler.jar > commonjs_out/closure_asserts_commonjs.js',
+       function (err, stdout, stderr) {
+    console.log(stdout);
+    console.log(stderr);
+    cb(err);
+  });
+});
+
+gulp.task('make_commonjs_out', ['dist', 'genproto_commonjs', 'commonjs_asserts'], function (cb) {
+  // TODO(haberman): minify this more aggressively.
+  // Will require proper externs/exports.
+  var cmd = "mkdir -p commonjs_out/binary && ";
+  function addTestFile(file) {
+    cmd += 'node commonjs/rewrite_tests_for_commonjs.js < ' + file +
+           ' > commonjs_out/' + file + '&& ';
+  }
+
+  glob.sync('*_test.js').forEach(addTestFile);
+  glob.sync('binary/*_test.js').forEach(addTestFile);
+
+  exec(cmd +
+       'cp commonjs/jasmine.json commonjs_out/jasmine.json && ' +
+       'cp google-protobuf.js commonjs_out',
+       function (err, stdout, stderr) {
+    console.log(stdout);
+    console.log(stderr);
+    cb(err);
+  });
+});
+
+gulp.task('deps', ['genproto_closure'], function (cb) {
   exec('./node_modules/google-closure-library/closure/bin/build/depswriter.py *.js binary/*.js > deps.js',
        function (err, stdout, stderr) {
     console.log(stdout);
     console.log(stderr);
     cb(err);
   });
-})
+});
 
-gulp.task('test', ['genproto', 'deps'], function (cb) {
+gulp.task('test_closure', ['genproto_closure', 'deps'], function (cb) {
   exec('JASMINE_CONFIG_PATH=jasmine.json ./node_modules/.bin/jasmine',
        function (err, stdout, stderr) {
     console.log(stdout);
@@ -27,3 +81,16 @@
     cb(err);
   });
 });
+
+gulp.task('test_commonjs', ['make_commonjs_out'], function (cb) {
+  exec('cd commonjs_out && JASMINE_CONFIG_PATH=jasmine.json NODE_PATH=. ../node_modules/.bin/jasmine',
+       function (err, stdout, stderr) {
+    console.log(stdout);
+    console.log(stderr);
+    cb(err);
+  });
+});
+
+gulp.task('test', ['test_closure', 'test_commonjs'], function(cb) {
+  cb();
+});
diff --git a/js/message_test.js b/js/message_test.js
index 971ea4f..f572188 100644
--- a/js/message_test.js
+++ b/js/message_test.js
@@ -34,35 +34,47 @@
 
 goog.require('goog.json');
 goog.require('goog.testing.asserts');
+
+// CommonJS-LoadFromFile: google-protobuf jspb
 goog.require('jspb.Message');
+
+// CommonJS-LoadFromFile: test5_pb proto.jspb.exttest.beta
 goog.require('proto.jspb.exttest.beta.floatingStrField');
+
+// CommonJS-LoadFromFile: test3_pb proto.jspb.exttest
 goog.require('proto.jspb.exttest.floatingMsgField');
+
+// CommonJS-LoadFromFile: test4_pb proto.jspb.exttest
 goog.require('proto.jspb.exttest.floatingMsgFieldTwo');
+
+// CommonJS-LoadFromFile: test_pb proto.jspb.test
 goog.require('proto.jspb.test.CloneExtension');
 goog.require('proto.jspb.test.Complex');
 goog.require('proto.jspb.test.DefaultValues');
 goog.require('proto.jspb.test.Empty');
 goog.require('proto.jspb.test.EnumContainer');
-goog.require('proto.jspb.test.ExtensionMessage');
-goog.require('proto.jspb.test.floatingMsgField');
 goog.require('proto.jspb.test.floatingStrField');
 goog.require('proto.jspb.test.HasExtensions');
 goog.require('proto.jspb.test.IndirectExtension');
 goog.require('proto.jspb.test.IsExtension');
 goog.require('proto.jspb.test.OptionalFields');
 goog.require('proto.jspb.test.OuterEnum');
+goog.require('proto.jspb.test.OuterMessage.Complex');
 goog.require('proto.jspb.test.simple1');
 goog.require('proto.jspb.test.Simple1');
 goog.require('proto.jspb.test.Simple2');
 goog.require('proto.jspb.test.SpecialCases');
 goog.require('proto.jspb.test.TestClone');
-goog.require('proto.jspb.test.TestExtensionsMessage');
 goog.require('proto.jspb.test.TestGroup');
 goog.require('proto.jspb.test.TestGroup1');
 goog.require('proto.jspb.test.TestMessageWithOneof');
 goog.require('proto.jspb.test.TestReservedNames');
 goog.require('proto.jspb.test.TestReservedNamesExtension');
 
+// CommonJS-LoadFromFile: test2_pb proto.jspb.test
+goog.require('proto.jspb.test.ExtensionMessage');
+goog.require('proto.jspb.test.TestExtensionsMessage');
+goog.require('proto.jspb.test.floatingMsgField');
 
 
 
@@ -86,6 +98,12 @@
     assertEquals('some_bytes', data.getBytesField());
   });
 
+  it('testNestedMessage', function() {
+    var msg = new proto.jspb.test.OuterMessage.Complex();
+    msg.setInnerComplexField(5);
+    assertObjectEquals({innerComplexField: 5}, msg.toObject());
+  });
+
   it('testComplexConversion', function() {
     var data1 = ['a',,, [, 11], [[, 22], [, 33]],, ['s1', 's2'],, 1];
     var data2 = ['a',,, [, 11], [[, 22], [, 33]],, ['s1', 's2'],, 1];
diff --git a/js/package.json b/js/package.json
index be93286..6418e50 100644
--- a/js/package.json
+++ b/js/package.json
@@ -2,13 +2,16 @@
   "name": "google-protobuf",
   "version": "3.0.0-alpha.5",
   "description": "Protocol Buffers for JavaScript",
-  "main": "debug.js",
+  "main": "google-protobuf.js",
   "dependencies": {
     "google-closure-library": "~20160125.0.0",
     "gulp": "~3.9.0",
     "jasmine": "~2.4.1"
   },
-  "devDependencies": {},
+  "devDependencies": {
+    "google-closure-compiler": "~20151216.2.0",
+    "glob": "~6.0.4"
+  },
   "scripts": {
     "test": "./node_modules/gulp/bin/gulp.js test"
   },
diff --git a/js/proto3_test.js b/js/proto3_test.js
index 8102bab..f886871 100644
--- a/js/proto3_test.js
+++ b/js/proto3_test.js
@@ -29,7 +29,11 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 goog.require('goog.testing.asserts');
+
+// CommonJS-LoadFromFile: testbinary_pb proto.jspb.test
 goog.require('proto.jspb.test.ForeignMessage');
+
+// CommonJS-LoadFromFile: proto3_test_pb proto.jspb.test
 goog.require('proto.jspb.test.Proto3Enum');
 goog.require('proto.jspb.test.TestProto3');
 
diff --git a/js/test.proto b/js/test.proto
index 5f9078e..3cea5f3 100644
--- a/js/test.proto
+++ b/js/test.proto
@@ -100,6 +100,13 @@
   repeated string a_repeated_string = 7;
 }
 
+message OuterMessage {
+  // Make sure this doesn't conflict with the other Complex message.
+  message Complex {
+    optional int32 inner_complex_field = 1;
+  }
+}
+
 message IsExtension {
   extend HasExtensions {
     optional IsExtension ext_field = 100;
diff --git a/objectivec/GPBCodedOutputStream.h b/objectivec/GPBCodedOutputStream.h
index a5aef17..0a47f1c 100644
--- a/objectivec/GPBCodedOutputStream.h
+++ b/objectivec/GPBCodedOutputStream.h
@@ -168,26 +168,26 @@
 - (void)writeEnumNoTag:(int32_t)value;
 
 - (void)writeString:(int32_t)fieldNumber value:(NSString *)value;
-- (void)writeStringArray:(int32_t)fieldNumber values:(NSArray *)values;
+- (void)writeStringArray:(int32_t)fieldNumber values:(NSArray<NSString*> *)values;
 - (void)writeStringNoTag:(NSString *)value;
 
 - (void)writeMessage:(int32_t)fieldNumber value:(GPBMessage *)value;
-- (void)writeMessageArray:(int32_t)fieldNumber values:(NSArray *)values;
+- (void)writeMessageArray:(int32_t)fieldNumber values:(NSArray<GPBMessage*> *)values;
 - (void)writeMessageNoTag:(GPBMessage *)value;
 
 - (void)writeBytes:(int32_t)fieldNumber value:(NSData *)value;
-- (void)writeBytesArray:(int32_t)fieldNumber values:(NSArray *)values;
+- (void)writeBytesArray:(int32_t)fieldNumber values:(NSArray<NSData*> *)values;
 - (void)writeBytesNoTag:(NSData *)value;
 
 - (void)writeGroup:(int32_t)fieldNumber
              value:(GPBMessage *)value;
-- (void)writeGroupArray:(int32_t)fieldNumber values:(NSArray *)values;
+- (void)writeGroupArray:(int32_t)fieldNumber values:(NSArray<GPBMessage*> *)values;
 - (void)writeGroupNoTag:(int32_t)fieldNumber
                   value:(GPBMessage *)value;
 
 - (void)writeUnknownGroup:(int32_t)fieldNumber
                     value:(GPBUnknownFieldSet *)value;
-- (void)writeUnknownGroupArray:(int32_t)fieldNumber values:(NSArray *)values;
+- (void)writeUnknownGroupArray:(int32_t)fieldNumber values:(NSArray<GPBUnknownFieldSet*> *)values;
 - (void)writeUnknownGroupNoTag:(int32_t)fieldNumber
                          value:(GPBUnknownFieldSet *)value;
 
@@ -306,17 +306,17 @@
 //%
 // Write methods for types that aren't in packed arrays.
 //%PDDM-DEFINE _WRITE_UNPACKABLE_DECLS(NAME, TYPE)
-//%- (void)write##NAME:(int32_t)fieldNumber value:(TYPE)value;
-//%- (void)write##NAME##Array:(int32_t)fieldNumber values:(NSArray *)values;
-//%- (void)write##NAME##NoTag:(TYPE)value;
+//%- (void)write##NAME:(int32_t)fieldNumber value:(TYPE *)value;
+//%- (void)write##NAME##Array:(int32_t)fieldNumber values:(NSArray<##TYPE##*> *)values;
+//%- (void)write##NAME##NoTag:(TYPE *)value;
 //%
 // Special write methods for Groups.
 //%PDDM-DEFINE _WRITE_GROUP_DECLS(NAME, TYPE)
 //%- (void)write##NAME:(int32_t)fieldNumber
-//%       NAME$S value:(TYPE)value;
-//%- (void)write##NAME##Array:(int32_t)fieldNumber values:(NSArray *)values;
+//%       NAME$S value:(TYPE *)value;
+//%- (void)write##NAME##Array:(int32_t)fieldNumber values:(NSArray<##TYPE##*> *)values;
 //%- (void)write##NAME##NoTag:(int32_t)fieldNumber
-//%            NAME$S value:(TYPE)value;
+//%            NAME$S value:(TYPE *)value;
 //%
 
 // One macro to hide it all up above.
@@ -335,8 +335,8 @@
 //%_WRITE_PACKABLE_DECLS(SFixed32, Int32, int32_t)
 //%_WRITE_PACKABLE_DECLS(Bool, Bool, BOOL)
 //%_WRITE_PACKABLE_DECLS(Enum, Enum, int32_t)
-//%_WRITE_UNPACKABLE_DECLS(String, NSString *)
-//%_WRITE_UNPACKABLE_DECLS(Message, GPBMessage *)
-//%_WRITE_UNPACKABLE_DECLS(Bytes, NSData *)
-//%_WRITE_GROUP_DECLS(Group, GPBMessage *)
-//%_WRITE_GROUP_DECLS(UnknownGroup, GPBUnknownFieldSet *)
+//%_WRITE_UNPACKABLE_DECLS(String, NSString)
+//%_WRITE_UNPACKABLE_DECLS(Message, GPBMessage)
+//%_WRITE_UNPACKABLE_DECLS(Bytes, NSData)
+//%_WRITE_GROUP_DECLS(Group, GPBMessage)
+//%_WRITE_GROUP_DECLS(UnknownGroup, GPBUnknownFieldSet)
diff --git a/objectivec/GPBDescriptor.h b/objectivec/GPBDescriptor.h
index 360afe9..8d8e975 100644
--- a/objectivec/GPBDescriptor.h
+++ b/objectivec/GPBDescriptor.h
@@ -55,9 +55,9 @@
 @interface GPBDescriptor : NSObject<NSCopying>
 
 @property(nonatomic, readonly, copy) NSString *name;
-@property(nonatomic, readonly, strong, nullable) NSArray *fields;
-@property(nonatomic, readonly, strong, nullable) NSArray *oneofs;
-@property(nonatomic, readonly, strong, nullable) NSArray *enums;
+@property(nonatomic, readonly, strong, nullable) NSArray<GPBFieldDescriptor*> *fields;
+@property(nonatomic, readonly, strong, nullable) NSArray<GPBOneofDescriptor*> *oneofs;
+@property(nonatomic, readonly, strong, nullable) NSArray<GPBEnumDescriptor*> *enums;
 @property(nonatomic, readonly, nullable) const GPBExtensionRange *extensionRanges;
 @property(nonatomic, readonly) NSUInteger extensionRangesCount;
 @property(nonatomic, readonly, assign) GPBFileDescriptor *file;
@@ -81,7 +81,7 @@
 
 @interface GPBOneofDescriptor : NSObject
 @property(nonatomic, readonly) NSString *name;
-@property(nonatomic, readonly) NSArray *fields;
+@property(nonatomic, readonly) NSArray<GPBFieldDescriptor*> *fields;
 
 - (nullable GPBFieldDescriptor *)fieldWithNumber:(uint32_t)fieldNumber;
 - (nullable GPBFieldDescriptor *)fieldWithName:(NSString *)name;
diff --git a/objectivec/GPBDictionary.h b/objectivec/GPBDictionary.h
index 6961cfc..3120814 100644
--- a/objectivec/GPBDictionary.h
+++ b/objectivec/GPBDictionary.h
@@ -355,33 +355,33 @@
 
 #pragma mark - UInt32 -> Object
 
-@interface GPBUInt32ObjectDictionary : NSObject <NSCopying>
+@interface GPBUInt32ObjectDictionary<__covariant ObjectType> : NSObject <NSCopying>
 
 @property(nonatomic, readonly) NSUInteger count;
 
 + (instancetype)dictionary;
-+ (instancetype)dictionaryWithObject:(id)object
++ (instancetype)dictionaryWithObject:(ObjectType)object
                               forKey:(uint32_t)key;
-+ (instancetype)dictionaryWithObjects:(const id GPB_UNSAFE_UNRETAINED [])objects
++ (instancetype)dictionaryWithObjects:(const ObjectType GPB_UNSAFE_UNRETAINED [])objects
                               forKeys:(const uint32_t [])keys
                                 count:(NSUInteger)count;
 + (instancetype)dictionaryWithDictionary:(GPBUInt32ObjectDictionary *)dictionary;
 + (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
 
-- (instancetype)initWithObjects:(const id GPB_UNSAFE_UNRETAINED [])objects
+- (instancetype)initWithObjects:(const ObjectType GPB_UNSAFE_UNRETAINED [])objects
                         forKeys:(const uint32_t [])keys
                           count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
 - (instancetype)initWithDictionary:(GPBUInt32ObjectDictionary *)dictionary;
 - (instancetype)initWithCapacity:(NSUInteger)numItems;
 
-- (id)objectForKey:(uint32_t)key;
+- (ObjectType)objectForKey:(uint32_t)key;
 
 - (void)enumerateKeysAndObjectsUsingBlock:
-    (void (^)(uint32_t key, id object, BOOL *stop))block;
+    (void (^)(uint32_t key, ObjectType object, BOOL *stop))block;
 
 - (void)addEntriesFromDictionary:(GPBUInt32ObjectDictionary *)otherDictionary;
 
-- (void)setObject:(id)object forKey:(uint32_t)key;
+- (void)setObject:(ObjectType)object forKey:(uint32_t)key;
 
 - (void)removeObjectForKey:(uint32_t)aKey;
 - (void)removeAll;
@@ -701,33 +701,33 @@
 
 #pragma mark - Int32 -> Object
 
-@interface GPBInt32ObjectDictionary : NSObject <NSCopying>
+@interface GPBInt32ObjectDictionary<__covariant ObjectType> : NSObject <NSCopying>
 
 @property(nonatomic, readonly) NSUInteger count;
 
 + (instancetype)dictionary;
-+ (instancetype)dictionaryWithObject:(id)object
++ (instancetype)dictionaryWithObject:(ObjectType)object
                               forKey:(int32_t)key;
-+ (instancetype)dictionaryWithObjects:(const id GPB_UNSAFE_UNRETAINED [])objects
++ (instancetype)dictionaryWithObjects:(const ObjectType GPB_UNSAFE_UNRETAINED [])objects
                               forKeys:(const int32_t [])keys
                                 count:(NSUInteger)count;
 + (instancetype)dictionaryWithDictionary:(GPBInt32ObjectDictionary *)dictionary;
 + (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
 
-- (instancetype)initWithObjects:(const id GPB_UNSAFE_UNRETAINED [])objects
+- (instancetype)initWithObjects:(const ObjectType GPB_UNSAFE_UNRETAINED [])objects
                         forKeys:(const int32_t [])keys
                           count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
 - (instancetype)initWithDictionary:(GPBInt32ObjectDictionary *)dictionary;
 - (instancetype)initWithCapacity:(NSUInteger)numItems;
 
-- (id)objectForKey:(int32_t)key;
+- (ObjectType)objectForKey:(int32_t)key;
 
 - (void)enumerateKeysAndObjectsUsingBlock:
-    (void (^)(int32_t key, id object, BOOL *stop))block;
+    (void (^)(int32_t key, ObjectType object, BOOL *stop))block;
 
 - (void)addEntriesFromDictionary:(GPBInt32ObjectDictionary *)otherDictionary;
 
-- (void)setObject:(id)object forKey:(int32_t)key;
+- (void)setObject:(ObjectType)object forKey:(int32_t)key;
 
 - (void)removeObjectForKey:(int32_t)aKey;
 - (void)removeAll;
@@ -1047,33 +1047,33 @@
 
 #pragma mark - UInt64 -> Object
 
-@interface GPBUInt64ObjectDictionary : NSObject <NSCopying>
+@interface GPBUInt64ObjectDictionary<__covariant ObjectType> : NSObject <NSCopying>
 
 @property(nonatomic, readonly) NSUInteger count;
 
 + (instancetype)dictionary;
-+ (instancetype)dictionaryWithObject:(id)object
++ (instancetype)dictionaryWithObject:(ObjectType)object
                               forKey:(uint64_t)key;
-+ (instancetype)dictionaryWithObjects:(const id GPB_UNSAFE_UNRETAINED [])objects
++ (instancetype)dictionaryWithObjects:(const ObjectType GPB_UNSAFE_UNRETAINED [])objects
                               forKeys:(const uint64_t [])keys
                                 count:(NSUInteger)count;
 + (instancetype)dictionaryWithDictionary:(GPBUInt64ObjectDictionary *)dictionary;
 + (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
 
-- (instancetype)initWithObjects:(const id GPB_UNSAFE_UNRETAINED [])objects
+- (instancetype)initWithObjects:(const ObjectType GPB_UNSAFE_UNRETAINED [])objects
                         forKeys:(const uint64_t [])keys
                           count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
 - (instancetype)initWithDictionary:(GPBUInt64ObjectDictionary *)dictionary;
 - (instancetype)initWithCapacity:(NSUInteger)numItems;
 
-- (id)objectForKey:(uint64_t)key;
+- (ObjectType)objectForKey:(uint64_t)key;
 
 - (void)enumerateKeysAndObjectsUsingBlock:
-    (void (^)(uint64_t key, id object, BOOL *stop))block;
+    (void (^)(uint64_t key, ObjectType object, BOOL *stop))block;
 
 - (void)addEntriesFromDictionary:(GPBUInt64ObjectDictionary *)otherDictionary;
 
-- (void)setObject:(id)object forKey:(uint64_t)key;
+- (void)setObject:(ObjectType)object forKey:(uint64_t)key;
 
 - (void)removeObjectForKey:(uint64_t)aKey;
 - (void)removeAll;
@@ -1393,33 +1393,33 @@
 
 #pragma mark - Int64 -> Object
 
-@interface GPBInt64ObjectDictionary : NSObject <NSCopying>
+@interface GPBInt64ObjectDictionary<__covariant ObjectType> : NSObject <NSCopying>
 
 @property(nonatomic, readonly) NSUInteger count;
 
 + (instancetype)dictionary;
-+ (instancetype)dictionaryWithObject:(id)object
++ (instancetype)dictionaryWithObject:(ObjectType)object
                               forKey:(int64_t)key;
-+ (instancetype)dictionaryWithObjects:(const id GPB_UNSAFE_UNRETAINED [])objects
++ (instancetype)dictionaryWithObjects:(const ObjectType GPB_UNSAFE_UNRETAINED [])objects
                               forKeys:(const int64_t [])keys
                                 count:(NSUInteger)count;
 + (instancetype)dictionaryWithDictionary:(GPBInt64ObjectDictionary *)dictionary;
 + (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
 
-- (instancetype)initWithObjects:(const id GPB_UNSAFE_UNRETAINED [])objects
+- (instancetype)initWithObjects:(const ObjectType GPB_UNSAFE_UNRETAINED [])objects
                         forKeys:(const int64_t [])keys
                           count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
 - (instancetype)initWithDictionary:(GPBInt64ObjectDictionary *)dictionary;
 - (instancetype)initWithCapacity:(NSUInteger)numItems;
 
-- (id)objectForKey:(int64_t)key;
+- (ObjectType)objectForKey:(int64_t)key;
 
 - (void)enumerateKeysAndObjectsUsingBlock:
-    (void (^)(int64_t key, id object, BOOL *stop))block;
+    (void (^)(int64_t key, ObjectType object, BOOL *stop))block;
 
 - (void)addEntriesFromDictionary:(GPBInt64ObjectDictionary *)otherDictionary;
 
-- (void)setObject:(id)object forKey:(int64_t)key;
+- (void)setObject:(ObjectType)object forKey:(int64_t)key;
 
 - (void)removeObjectForKey:(int64_t)aKey;
 - (void)removeAll;
@@ -1739,33 +1739,33 @@
 
 #pragma mark - Bool -> Object
 
-@interface GPBBoolObjectDictionary : NSObject <NSCopying>
+@interface GPBBoolObjectDictionary<__covariant ObjectType> : NSObject <NSCopying>
 
 @property(nonatomic, readonly) NSUInteger count;
 
 + (instancetype)dictionary;
-+ (instancetype)dictionaryWithObject:(id)object
++ (instancetype)dictionaryWithObject:(ObjectType)object
                               forKey:(BOOL)key;
-+ (instancetype)dictionaryWithObjects:(const id GPB_UNSAFE_UNRETAINED [])objects
++ (instancetype)dictionaryWithObjects:(const ObjectType GPB_UNSAFE_UNRETAINED [])objects
                               forKeys:(const BOOL [])keys
                                 count:(NSUInteger)count;
 + (instancetype)dictionaryWithDictionary:(GPBBoolObjectDictionary *)dictionary;
 + (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
 
-- (instancetype)initWithObjects:(const id GPB_UNSAFE_UNRETAINED [])objects
+- (instancetype)initWithObjects:(const ObjectType GPB_UNSAFE_UNRETAINED [])objects
                         forKeys:(const BOOL [])keys
                           count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
 - (instancetype)initWithDictionary:(GPBBoolObjectDictionary *)dictionary;
 - (instancetype)initWithCapacity:(NSUInteger)numItems;
 
-- (id)objectForKey:(BOOL)key;
+- (ObjectType)objectForKey:(BOOL)key;
 
 - (void)enumerateKeysAndObjectsUsingBlock:
-    (void (^)(BOOL key, id object, BOOL *stop))block;
+    (void (^)(BOOL key, ObjectType object, BOOL *stop))block;
 
 - (void)addEntriesFromDictionary:(GPBBoolObjectDictionary *)otherDictionary;
 
-- (void)setObject:(id)object forKey:(BOOL)key;
+- (void)setObject:(ObjectType)object forKey:(BOOL)key;
 
 - (void)removeObjectForKey:(BOOL)aKey;
 - (void)removeAll;
@@ -2096,7 +2096,7 @@
 //%DICTIONARY_POD_INTERFACES_FOR_KEY(String, NSString, *, OBJECT)
 //%PDDM-DEFINE DICTIONARY_INTERFACES_FOR_POD_KEY(KEY_NAME, KEY_TYPE)
 //%DICTIONARY_POD_INTERFACES_FOR_KEY(KEY_NAME, KEY_TYPE, , POD)
-//%DICTIONARY_POD_KEY_TO_OBJECT_INTERFACE(KEY_NAME, KEY_TYPE, Object, id)
+//%DICTIONARY_POD_KEY_TO_OBJECT_INTERFACE(KEY_NAME, KEY_TYPE, Object, ObjectType)
 //%PDDM-DEFINE DICTIONARY_POD_INTERFACES_FOR_KEY(KEY_NAME, KEY_TYPE, KisP, KHELPER)
 //%DICTIONARY_KEY_TO_POD_INTERFACE(KEY_NAME, KEY_TYPE, KisP, KHELPER, UInt32, uint32_t)
 //%DICTIONARY_KEY_TO_POD_INTERFACE(KEY_NAME, KEY_TYPE, KisP, KHELPER, Int32, int32_t)
@@ -2122,10 +2122,16 @@
 // Nothing
 //%PDDM-DEFINE ARRAY_ARG_MODIFIEROBJECT()
 //%GPB_UNSAFE_UNRETAINED ##
+//%PDDM-DEFINE DICTIONARY_CLASS_DECLPOD(KEY_NAME, VALUE_NAME, VALUE_TYPE)
+//%GPB##KEY_NAME##VALUE_NAME##Dictionary
+//%PDDM-DEFINE DICTIONARY_CLASS_DECLEnum(KEY_NAME, VALUE_NAME, VALUE_TYPE)
+//%GPB##KEY_NAME##VALUE_NAME##Dictionary
+//%PDDM-DEFINE DICTIONARY_CLASS_DECLOBJECT(KEY_NAME, VALUE_NAME, VALUE_TYPE)
+//%GPB##KEY_NAME##VALUE_NAME##Dictionary<__covariant VALUE_TYPE>
 //%PDDM-DEFINE DICTIONARY_COMMON_INTERFACE(KEY_NAME, KEY_TYPE, KisP, KHELPER, VALUE_NAME, VALUE_TYPE, VHELPER, VNAME)
 //%#pragma mark - KEY_NAME -> VALUE_NAME
 //%
-//%@interface GPB##KEY_NAME##VALUE_NAME##Dictionary : NSObject <NSCopying>
+//%@interface DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) : NSObject <NSCopying>
 //%
 //%@property(nonatomic, readonly) NSUInteger count;
 //%
diff --git a/objectivec/GPBUnknownField.h b/objectivec/GPBUnknownField.h
index 12d72a9..43709ee 100644
--- a/objectivec/GPBUnknownField.h
+++ b/objectivec/GPBUnknownField.h
@@ -45,8 +45,8 @@
 @property(nonatomic, readonly, strong) GPBUInt64Array *varintList;
 @property(nonatomic, readonly, strong) GPBUInt32Array *fixed32List;
 @property(nonatomic, readonly, strong) GPBUInt64Array *fixed64List;
-@property(nonatomic, readonly, strong) NSArray *lengthDelimitedList;  // NSData
-@property(nonatomic, readonly, strong) NSArray *groupList;  // GPBUnknownFieldSet
+@property(nonatomic, readonly, strong) NSArray<NSData*> *lengthDelimitedList;
+@property(nonatomic, readonly, strong) NSArray<GPBUnknownFieldSet*> *groupList;
 
 // Only one of these should be used per Field.
 - (void)addVarint:(uint64_t)value;
diff --git a/objectivec/GPBUnknownField.m b/objectivec/GPBUnknownField.m
index c49c0df..22ed66a 100644
--- a/objectivec/GPBUnknownField.m
+++ b/objectivec/GPBUnknownField.m
@@ -39,8 +39,8 @@
   GPBUInt64Array *mutableVarintList_;
   GPBUInt32Array *mutableFixed32List_;
   GPBUInt64Array *mutableFixed64List_;
-  NSMutableArray *mutableLengthDelimitedList_;
-  NSMutableArray *mutableGroupList_;
+  NSMutableArray<NSData*> *mutableLengthDelimitedList_;
+  NSMutableArray<GPBUnknownFieldSet*> *mutableGroupList_;
 }
 
 @synthesize number = number_;
diff --git a/objectivec/GPBUnknownFieldSet.h b/objectivec/GPBUnknownFieldSet.h
index d785ca1..8db0132 100644
--- a/objectivec/GPBUnknownFieldSet.h
+++ b/objectivec/GPBUnknownFieldSet.h
@@ -42,8 +42,8 @@
 
 - (void)addField:(GPBUnknownField *)field;
 
-// Returns an NSArray of the GPBFields sorted by the field numbers.
-- (NSArray *)sortedFields;
+// Returns an NSArray of the GPBUnknownFields sorted by the field numbers.
+- (NSArray<GPBUnknownField*> *)sortedFields;
 
 @end
 
diff --git a/objectivec/Tests/GPBDictionaryTests+Bool.m b/objectivec/Tests/GPBDictionaryTests+Bool.m
index 8b1900f..afa3d11 100644
--- a/objectivec/Tests/GPBDictionaryTests+Bool.m
+++ b/objectivec/Tests/GPBDictionaryTests+Bool.m
@@ -2147,7 +2147,7 @@
 
 @end
 
-//%PDDM-EXPAND TESTS_FOR_BOOL_KEY_OBJECT_VALUE(Object, id, @"abc", @"def")
+//%PDDM-EXPAND TESTS_FOR_BOOL_KEY_OBJECT_VALUE(Object, NSString*, @"abc", @"def")
 // This block of code is generated, do not edit it directly.
 
 #pragma mark - Bool -> Object
@@ -2158,11 +2158,11 @@
 @implementation GPBBoolObjectDictionaryTests
 
 - (void)testEmpty {
-  GPBBoolObjectDictionary *dict = [[GPBBoolObjectDictionary alloc] init];
+  GPBBoolObjectDictionary<NSString*> *dict = [[GPBBoolObjectDictionary alloc] init];
   XCTAssertNotNil(dict);
   XCTAssertEqual(dict.count, 0U);
   XCTAssertNil([dict objectForKey:YES]);
-  [dict enumerateKeysAndObjectsUsingBlock:^(BOOL aKey, id aObject, BOOL *stop) {
+  [dict enumerateKeysAndObjectsUsingBlock:^(BOOL aKey, NSString* aObject, BOOL *stop) {
     #pragma unused(aKey, aObject, stop)
     XCTFail(@"Shouldn't get here!");
   }];
@@ -2170,12 +2170,12 @@
 }
 
 - (void)testOne {
-  GPBBoolObjectDictionary *dict = [GPBBoolObjectDictionary dictionaryWithObject:@"abc" forKey:YES];
+  GPBBoolObjectDictionary<NSString*> *dict = [GPBBoolObjectDictionary dictionaryWithObject:@"abc" forKey:YES];
   XCTAssertNotNil(dict);
   XCTAssertEqual(dict.count, 1U);
   XCTAssertEqualObjects([dict objectForKey:YES], @"abc");
   XCTAssertNil([dict objectForKey:NO]);
-  [dict enumerateKeysAndObjectsUsingBlock:^(BOOL aKey, id aObject, BOOL *stop) {
+  [dict enumerateKeysAndObjectsUsingBlock:^(BOOL aKey, NSString* aObject, BOOL *stop) {
     XCTAssertEqual(aKey, YES);
     XCTAssertEqualObjects(aObject, @"abc");
     XCTAssertNotEqual(stop, NULL);
@@ -2184,8 +2184,8 @@
 
 - (void)testBasics {
   const BOOL kKeys[] = { YES, NO };
-  const id kObjects[] = { @"abc", @"def" };
-  GPBBoolObjectDictionary *dict =
+  const NSString* kObjects[] = { @"abc", @"def" };
+  GPBBoolObjectDictionary<NSString*> *dict =
       [[GPBBoolObjectDictionary alloc] initWithObjects:kObjects
                                                forKeys:kKeys
                                                  count:GPBARRAYSIZE(kObjects)];
@@ -2196,8 +2196,8 @@
 
   __block NSUInteger idx = 0;
   BOOL *seenKeys = malloc(2 * sizeof(BOOL));
-  id *seenObjects = malloc(2 * sizeof(id));
-  [dict enumerateKeysAndObjectsUsingBlock:^(BOOL aKey, id aObject, BOOL *stop) {
+  NSString* *seenObjects = malloc(2 * sizeof(NSString*));
+  [dict enumerateKeysAndObjectsUsingBlock:^(BOOL aKey, NSString* aObject, BOOL *stop) {
     XCTAssertLessThan(idx, 2U);
     seenKeys[idx] = aKey;
     seenObjects[idx] = aObject;
@@ -2219,7 +2219,7 @@
 
   // Stopping the enumeration.
   idx = 0;
-  [dict enumerateKeysAndObjectsUsingBlock:^(BOOL aKey, id aObject, BOOL *stop) {
+  [dict enumerateKeysAndObjectsUsingBlock:^(BOOL aKey, NSString* aObject, BOOL *stop) {
     #pragma unused(aKey, aObject)
     if (idx == 0) *stop = YES;
     XCTAssertNotEqual(idx, 2U);
@@ -2231,30 +2231,30 @@
 - (void)testEquality {
   const BOOL kKeys1[] = { YES, NO };
   const BOOL kKeys2[] = { NO, YES };
-  const id kObjects1[] = { @"abc", @"def" };
-  const id kObjects2[] = { @"def", @"abc" };
-  const id kObjects3[] = { @"def" };
-  GPBBoolObjectDictionary *dict1 =
+  const NSString* kObjects1[] = { @"abc", @"def" };
+  const NSString* kObjects2[] = { @"def", @"abc" };
+  const NSString* kObjects3[] = { @"def" };
+  GPBBoolObjectDictionary<NSString*> *dict1 =
       [[GPBBoolObjectDictionary alloc] initWithObjects:kObjects1
                                                forKeys:kKeys1
                                                  count:GPBARRAYSIZE(kObjects1)];
   XCTAssertNotNil(dict1);
-  GPBBoolObjectDictionary *dict1prime =
+  GPBBoolObjectDictionary<NSString*> *dict1prime =
       [[GPBBoolObjectDictionary alloc] initWithObjects:kObjects1
                                                forKeys:kKeys1
                                                  count:GPBARRAYSIZE(kObjects1)];
   XCTAssertNotNil(dict1prime);
-  GPBBoolObjectDictionary *dict2 =
+  GPBBoolObjectDictionary<NSString*> *dict2 =
       [[GPBBoolObjectDictionary alloc] initWithObjects:kObjects2
                                                forKeys:kKeys1
                                                  count:GPBARRAYSIZE(kObjects2)];
   XCTAssertNotNil(dict2);
-  GPBBoolObjectDictionary *dict3 =
+  GPBBoolObjectDictionary<NSString*> *dict3 =
       [[GPBBoolObjectDictionary alloc] initWithObjects:kObjects1
                                                forKeys:kKeys2
                                                  count:GPBARRAYSIZE(kObjects1)];
   XCTAssertNotNil(dict3);
-  GPBBoolObjectDictionary *dict4 =
+  GPBBoolObjectDictionary<NSString*> *dict4 =
       [[GPBBoolObjectDictionary alloc] initWithObjects:kObjects3
                                                forKeys:kKeys1
                                                  count:GPBARRAYSIZE(kObjects3)];
@@ -2284,14 +2284,14 @@
 
 - (void)testCopy {
   const BOOL kKeys[] = { YES, NO };
-  const id kObjects[] = { @"abc", @"def" };
-  GPBBoolObjectDictionary *dict =
+  const NSString* kObjects[] = { @"abc", @"def" };
+  GPBBoolObjectDictionary<NSString*> *dict =
       [[GPBBoolObjectDictionary alloc] initWithObjects:kObjects
                                                forKeys:kKeys
                                                  count:GPBARRAYSIZE(kObjects)];
   XCTAssertNotNil(dict);
 
-  GPBBoolObjectDictionary *dict2 = [dict copy];
+  GPBBoolObjectDictionary<NSString*> *dict2 = [dict copy];
   XCTAssertNotNil(dict2);
 
   // Should be new object but equal.
@@ -2305,14 +2305,14 @@
 
 - (void)testDictionaryFromDictionary {
   const BOOL kKeys[] = { YES, NO };
-  const id kObjects[] = { @"abc", @"def" };
-  GPBBoolObjectDictionary *dict =
+  const NSString* kObjects[] = { @"abc", @"def" };
+  GPBBoolObjectDictionary<NSString*> *dict =
       [[GPBBoolObjectDictionary alloc] initWithObjects:kObjects
                                                forKeys:kKeys
                                                  count:GPBARRAYSIZE(kObjects)];
   XCTAssertNotNil(dict);
 
-  GPBBoolObjectDictionary *dict2 =
+  GPBBoolObjectDictionary<NSString*> *dict2 =
       [GPBBoolObjectDictionary dictionaryWithDictionary:dict];
   XCTAssertNotNil(dict2);
 
@@ -2323,7 +2323,7 @@
 }
 
 - (void)testAdds {
-  GPBBoolObjectDictionary *dict = [GPBBoolObjectDictionary dictionary];
+  GPBBoolObjectDictionary<NSString*> *dict = [GPBBoolObjectDictionary dictionary];
   XCTAssertNotNil(dict);
 
   XCTAssertEqual(dict.count, 0U);
@@ -2331,8 +2331,8 @@
   XCTAssertEqual(dict.count, 1U);
 
   const BOOL kKeys[] = { NO };
-  const id kObjects[] = { @"def" };
-  GPBBoolObjectDictionary *dict2 =
+  const NSString* kObjects[] = { @"def" };
+  GPBBoolObjectDictionary<NSString*> *dict2 =
       [[GPBBoolObjectDictionary alloc] initWithObjects:kObjects
                                                forKeys:kKeys
                                                  count:GPBARRAYSIZE(kObjects)];
@@ -2347,8 +2347,8 @@
 
 - (void)testRemove {
   const BOOL kKeys[] = { YES, NO};
-  const id kObjects[] = { @"abc", @"def" };
-  GPBBoolObjectDictionary *dict =
+  const NSString* kObjects[] = { @"abc", @"def" };
+  GPBBoolObjectDictionary<NSString*> *dict =
       [[GPBBoolObjectDictionary alloc] initWithObjects:kObjects
                                         forKeys:kKeys
                                           count:GPBARRAYSIZE(kObjects)];
@@ -2375,8 +2375,8 @@
 
 - (void)testInplaceMutation {
   const BOOL kKeys[] = { YES, NO };
-  const id kObjects[] = { @"abc", @"def" };
-  GPBBoolObjectDictionary *dict =
+  const NSString* kObjects[] = { @"abc", @"def" };
+  GPBBoolObjectDictionary<NSString*> *dict =
       [[GPBBoolObjectDictionary alloc] initWithObjects:kObjects
                                         forKeys:kKeys
                                           count:GPBARRAYSIZE(kObjects)];
@@ -2396,8 +2396,8 @@
   XCTAssertEqualObjects([dict objectForKey:NO], @"abc");
 
   const BOOL kKeys2[] = { NO, YES };
-  const id kObjects2[] = { @"def", @"abc" };
-  GPBBoolObjectDictionary *dict2 =
+  const NSString* kObjects2[] = { @"def", @"abc" };
+  GPBBoolObjectDictionary<NSString*> *dict2 =
       [[GPBBoolObjectDictionary alloc] initWithObjects:kObjects2
                                                forKeys:kKeys2
                                                  count:GPBARRAYSIZE(kObjects2)];
diff --git a/objectivec/Tests/GPBDictionaryTests+Int32.m b/objectivec/Tests/GPBDictionaryTests+Int32.m
index 21d3f07..54dd2ed 100644
--- a/objectivec/Tests/GPBDictionaryTests+Int32.m
+++ b/objectivec/Tests/GPBDictionaryTests+Int32.m
@@ -3363,11 +3363,11 @@
 @implementation GPBInt32ObjectDictionaryTests
 
 - (void)testEmpty {
-  GPBInt32ObjectDictionary *dict = [[GPBInt32ObjectDictionary alloc] init];
+  GPBInt32ObjectDictionary<NSString*> *dict = [[GPBInt32ObjectDictionary alloc] init];
   XCTAssertNotNil(dict);
   XCTAssertEqual(dict.count, 0U);
   XCTAssertNil([dict objectForKey:11]);
-  [dict enumerateKeysAndObjectsUsingBlock:^(int32_t aKey, id aObject, BOOL *stop) {
+  [dict enumerateKeysAndObjectsUsingBlock:^(int32_t aKey, NSString* aObject, BOOL *stop) {
     #pragma unused(aKey, aObject, stop)
     XCTFail(@"Shouldn't get here!");
   }];
@@ -3375,12 +3375,12 @@
 }
 
 - (void)testOne {
-  GPBInt32ObjectDictionary *dict = [GPBInt32ObjectDictionary dictionaryWithObject:@"abc" forKey:11];
+  GPBInt32ObjectDictionary<NSString*> *dict = [GPBInt32ObjectDictionary dictionaryWithObject:@"abc" forKey:11];
   XCTAssertNotNil(dict);
   XCTAssertEqual(dict.count, 1U);
   XCTAssertEqualObjects([dict objectForKey:11], @"abc");
   XCTAssertNil([dict objectForKey:12]);
-  [dict enumerateKeysAndObjectsUsingBlock:^(int32_t aKey, id aObject, BOOL *stop) {
+  [dict enumerateKeysAndObjectsUsingBlock:^(int32_t aKey, NSString* aObject, BOOL *stop) {
     XCTAssertEqual(aKey, 11);
     XCTAssertEqualObjects(aObject, @"abc");
     XCTAssertNotEqual(stop, NULL);
@@ -3389,8 +3389,8 @@
 
 - (void)testBasics {
   const int32_t kKeys[] = { 11, 12, 13 };
-  const id kObjects[] = { @"abc", @"def", @"ghi" };
-  GPBInt32ObjectDictionary *dict =
+  const NSString* kObjects[] = { @"abc", @"def", @"ghi" };
+  GPBInt32ObjectDictionary<NSString*> *dict =
       [[GPBInt32ObjectDictionary alloc] initWithObjects:kObjects
                                                 forKeys:kKeys
                                                   count:GPBARRAYSIZE(kObjects)];
@@ -3403,8 +3403,8 @@
 
   __block NSUInteger idx = 0;
   int32_t *seenKeys = malloc(3 * sizeof(int32_t));
-  id *seenObjects = malloc(3 * sizeof(id));
-  [dict enumerateKeysAndObjectsUsingBlock:^(int32_t aKey, id aObject, BOOL *stop) {
+  NSString* *seenObjects = malloc(3 * sizeof(NSString*));
+  [dict enumerateKeysAndObjectsUsingBlock:^(int32_t aKey, NSString* aObject, BOOL *stop) {
     XCTAssertLessThan(idx, 3U);
     seenKeys[idx] = aKey;
     seenObjects[idx] = aObject;
@@ -3426,7 +3426,7 @@
 
   // Stopping the enumeration.
   idx = 0;
-  [dict enumerateKeysAndObjectsUsingBlock:^(int32_t aKey, id aObject, BOOL *stop) {
+  [dict enumerateKeysAndObjectsUsingBlock:^(int32_t aKey, NSString* aObject, BOOL *stop) {
     #pragma unused(aKey, aObject)
     if (idx == 1) *stop = YES;
     XCTAssertNotEqual(idx, 2U);
@@ -3438,30 +3438,30 @@
 - (void)testEquality {
   const int32_t kKeys1[] = { 11, 12, 13, 14 };
   const int32_t kKeys2[] = { 12, 11, 14 };
-  const id kObjects1[] = { @"abc", @"def", @"ghi" };
-  const id kObjects2[] = { @"abc", @"jkl", @"ghi" };
-  const id kObjects3[] = { @"abc", @"def", @"ghi", @"jkl" };
-  GPBInt32ObjectDictionary *dict1 =
+  const NSString* kObjects1[] = { @"abc", @"def", @"ghi" };
+  const NSString* kObjects2[] = { @"abc", @"jkl", @"ghi" };
+  const NSString* kObjects3[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBInt32ObjectDictionary<NSString*> *dict1 =
       [[GPBInt32ObjectDictionary alloc] initWithObjects:kObjects1
                                                 forKeys:kKeys1
                                                   count:GPBARRAYSIZE(kObjects1)];
   XCTAssertNotNil(dict1);
-  GPBInt32ObjectDictionary *dict1prime =
+  GPBInt32ObjectDictionary<NSString*> *dict1prime =
       [[GPBInt32ObjectDictionary alloc] initWithObjects:kObjects1
                                                 forKeys:kKeys1
                                                   count:GPBARRAYSIZE(kObjects1)];
   XCTAssertNotNil(dict1prime);
-  GPBInt32ObjectDictionary *dict2 =
+  GPBInt32ObjectDictionary<NSString*> *dict2 =
       [[GPBInt32ObjectDictionary alloc] initWithObjects:kObjects2
                                                 forKeys:kKeys1
                                                   count:GPBARRAYSIZE(kObjects2)];
   XCTAssertNotNil(dict2);
-  GPBInt32ObjectDictionary *dict3 =
+  GPBInt32ObjectDictionary<NSString*> *dict3 =
       [[GPBInt32ObjectDictionary alloc] initWithObjects:kObjects1
                                                 forKeys:kKeys2
                                                   count:GPBARRAYSIZE(kObjects1)];
   XCTAssertNotNil(dict3);
-  GPBInt32ObjectDictionary *dict4 =
+  GPBInt32ObjectDictionary<NSString*> *dict4 =
       [[GPBInt32ObjectDictionary alloc] initWithObjects:kObjects3
                                                 forKeys:kKeys1
                                                   count:GPBARRAYSIZE(kObjects3)];
@@ -3491,14 +3491,14 @@
 
 - (void)testCopy {
   const int32_t kKeys[] = { 11, 12, 13, 14 };
-  const id kObjects[] = { @"abc", @"def", @"ghi", @"jkl" };
-  GPBInt32ObjectDictionary *dict =
+  const NSString* kObjects[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBInt32ObjectDictionary<NSString*> *dict =
       [[GPBInt32ObjectDictionary alloc] initWithObjects:kObjects
                                                 forKeys:kKeys
                                                   count:GPBARRAYSIZE(kObjects)];
   XCTAssertNotNil(dict);
 
-  GPBInt32ObjectDictionary *dict2 = [dict copy];
+  GPBInt32ObjectDictionary<NSString*> *dict2 = [dict copy];
   XCTAssertNotNil(dict2);
 
   // Should be new object but equal.
@@ -3512,14 +3512,14 @@
 
 - (void)testDictionaryFromDictionary {
   const int32_t kKeys[] = { 11, 12, 13, 14 };
-  const id kObjects[] = { @"abc", @"def", @"ghi", @"jkl" };
-  GPBInt32ObjectDictionary *dict =
+  const NSString* kObjects[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBInt32ObjectDictionary<NSString*> *dict =
       [[GPBInt32ObjectDictionary alloc] initWithObjects:kObjects
                                                 forKeys:kKeys
                                                   count:GPBARRAYSIZE(kObjects)];
   XCTAssertNotNil(dict);
 
-  GPBInt32ObjectDictionary *dict2 =
+  GPBInt32ObjectDictionary<NSString*> *dict2 =
       [GPBInt32ObjectDictionary dictionaryWithDictionary:dict];
   XCTAssertNotNil(dict2);
 
@@ -3530,7 +3530,7 @@
 }
 
 - (void)testAdds {
-  GPBInt32ObjectDictionary *dict = [GPBInt32ObjectDictionary dictionary];
+  GPBInt32ObjectDictionary<NSString*> *dict = [GPBInt32ObjectDictionary dictionary];
   XCTAssertNotNil(dict);
 
   XCTAssertEqual(dict.count, 0U);
@@ -3538,8 +3538,8 @@
   XCTAssertEqual(dict.count, 1U);
 
   const int32_t kKeys[] = { 12, 13, 14 };
-  const id kObjects[] = { @"def", @"ghi", @"jkl" };
-  GPBInt32ObjectDictionary *dict2 =
+  const NSString* kObjects[] = { @"def", @"ghi", @"jkl" };
+  GPBInt32ObjectDictionary<NSString*> *dict2 =
       [[GPBInt32ObjectDictionary alloc] initWithObjects:kObjects
                                                 forKeys:kKeys
                                                   count:GPBARRAYSIZE(kObjects)];
@@ -3556,8 +3556,8 @@
 
 - (void)testRemove {
   const int32_t kKeys[] = { 11, 12, 13, 14 };
-  const id kObjects[] = { @"abc", @"def", @"ghi", @"jkl" };
-  GPBInt32ObjectDictionary *dict =
+  const NSString* kObjects[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBInt32ObjectDictionary<NSString*> *dict =
       [[GPBInt32ObjectDictionary alloc] initWithObjects:kObjects
                                          forKeys:kKeys
                                            count:GPBARRAYSIZE(kObjects)];
@@ -3597,8 +3597,8 @@
 
 - (void)testInplaceMutation {
   const int32_t kKeys[] = { 11, 12, 13, 14 };
-  const id kObjects[] = { @"abc", @"def", @"ghi", @"jkl" };
-  GPBInt32ObjectDictionary *dict =
+  const NSString* kObjects[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBInt32ObjectDictionary<NSString*> *dict =
       [[GPBInt32ObjectDictionary alloc] initWithObjects:kObjects
                                          forKeys:kKeys
                                            count:GPBARRAYSIZE(kObjects)];
@@ -3624,8 +3624,8 @@
   XCTAssertEqualObjects([dict objectForKey:14], @"def");
 
   const int32_t kKeys2[] = { 12, 13 };
-  const id kObjects2[] = { @"ghi", @"abc" };
-  GPBInt32ObjectDictionary *dict2 =
+  const NSString* kObjects2[] = { @"ghi", @"abc" };
+  GPBInt32ObjectDictionary<NSString*> *dict2 =
       [[GPBInt32ObjectDictionary alloc] initWithObjects:kObjects2
                                                 forKeys:kKeys2
                                                   count:GPBARRAYSIZE(kObjects2)];
diff --git a/objectivec/Tests/GPBDictionaryTests+Int64.m b/objectivec/Tests/GPBDictionaryTests+Int64.m
index 27f77f2..66bc648 100644
--- a/objectivec/Tests/GPBDictionaryTests+Int64.m
+++ b/objectivec/Tests/GPBDictionaryTests+Int64.m
@@ -3363,11 +3363,11 @@
 @implementation GPBInt64ObjectDictionaryTests
 
 - (void)testEmpty {
-  GPBInt64ObjectDictionary *dict = [[GPBInt64ObjectDictionary alloc] init];
+  GPBInt64ObjectDictionary<NSString*> *dict = [[GPBInt64ObjectDictionary alloc] init];
   XCTAssertNotNil(dict);
   XCTAssertEqual(dict.count, 0U);
   XCTAssertNil([dict objectForKey:21LL]);
-  [dict enumerateKeysAndObjectsUsingBlock:^(int64_t aKey, id aObject, BOOL *stop) {
+  [dict enumerateKeysAndObjectsUsingBlock:^(int64_t aKey, NSString* aObject, BOOL *stop) {
     #pragma unused(aKey, aObject, stop)
     XCTFail(@"Shouldn't get here!");
   }];
@@ -3375,12 +3375,12 @@
 }
 
 - (void)testOne {
-  GPBInt64ObjectDictionary *dict = [GPBInt64ObjectDictionary dictionaryWithObject:@"abc" forKey:21LL];
+  GPBInt64ObjectDictionary<NSString*> *dict = [GPBInt64ObjectDictionary dictionaryWithObject:@"abc" forKey:21LL];
   XCTAssertNotNil(dict);
   XCTAssertEqual(dict.count, 1U);
   XCTAssertEqualObjects([dict objectForKey:21LL], @"abc");
   XCTAssertNil([dict objectForKey:22LL]);
-  [dict enumerateKeysAndObjectsUsingBlock:^(int64_t aKey, id aObject, BOOL *stop) {
+  [dict enumerateKeysAndObjectsUsingBlock:^(int64_t aKey, NSString* aObject, BOOL *stop) {
     XCTAssertEqual(aKey, 21LL);
     XCTAssertEqualObjects(aObject, @"abc");
     XCTAssertNotEqual(stop, NULL);
@@ -3389,8 +3389,8 @@
 
 - (void)testBasics {
   const int64_t kKeys[] = { 21LL, 22LL, 23LL };
-  const id kObjects[] = { @"abc", @"def", @"ghi" };
-  GPBInt64ObjectDictionary *dict =
+  const NSString* kObjects[] = { @"abc", @"def", @"ghi" };
+  GPBInt64ObjectDictionary<NSString*> *dict =
       [[GPBInt64ObjectDictionary alloc] initWithObjects:kObjects
                                                 forKeys:kKeys
                                                   count:GPBARRAYSIZE(kObjects)];
@@ -3403,8 +3403,8 @@
 
   __block NSUInteger idx = 0;
   int64_t *seenKeys = malloc(3 * sizeof(int64_t));
-  id *seenObjects = malloc(3 * sizeof(id));
-  [dict enumerateKeysAndObjectsUsingBlock:^(int64_t aKey, id aObject, BOOL *stop) {
+  NSString* *seenObjects = malloc(3 * sizeof(NSString*));
+  [dict enumerateKeysAndObjectsUsingBlock:^(int64_t aKey, NSString* aObject, BOOL *stop) {
     XCTAssertLessThan(idx, 3U);
     seenKeys[idx] = aKey;
     seenObjects[idx] = aObject;
@@ -3426,7 +3426,7 @@
 
   // Stopping the enumeration.
   idx = 0;
-  [dict enumerateKeysAndObjectsUsingBlock:^(int64_t aKey, id aObject, BOOL *stop) {
+  [dict enumerateKeysAndObjectsUsingBlock:^(int64_t aKey, NSString* aObject, BOOL *stop) {
     #pragma unused(aKey, aObject)
     if (idx == 1) *stop = YES;
     XCTAssertNotEqual(idx, 2U);
@@ -3438,30 +3438,30 @@
 - (void)testEquality {
   const int64_t kKeys1[] = { 21LL, 22LL, 23LL, 24LL };
   const int64_t kKeys2[] = { 22LL, 21LL, 24LL };
-  const id kObjects1[] = { @"abc", @"def", @"ghi" };
-  const id kObjects2[] = { @"abc", @"jkl", @"ghi" };
-  const id kObjects3[] = { @"abc", @"def", @"ghi", @"jkl" };
-  GPBInt64ObjectDictionary *dict1 =
+  const NSString* kObjects1[] = { @"abc", @"def", @"ghi" };
+  const NSString* kObjects2[] = { @"abc", @"jkl", @"ghi" };
+  const NSString* kObjects3[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBInt64ObjectDictionary<NSString*> *dict1 =
       [[GPBInt64ObjectDictionary alloc] initWithObjects:kObjects1
                                                 forKeys:kKeys1
                                                   count:GPBARRAYSIZE(kObjects1)];
   XCTAssertNotNil(dict1);
-  GPBInt64ObjectDictionary *dict1prime =
+  GPBInt64ObjectDictionary<NSString*> *dict1prime =
       [[GPBInt64ObjectDictionary alloc] initWithObjects:kObjects1
                                                 forKeys:kKeys1
                                                   count:GPBARRAYSIZE(kObjects1)];
   XCTAssertNotNil(dict1prime);
-  GPBInt64ObjectDictionary *dict2 =
+  GPBInt64ObjectDictionary<NSString*> *dict2 =
       [[GPBInt64ObjectDictionary alloc] initWithObjects:kObjects2
                                                 forKeys:kKeys1
                                                   count:GPBARRAYSIZE(kObjects2)];
   XCTAssertNotNil(dict2);
-  GPBInt64ObjectDictionary *dict3 =
+  GPBInt64ObjectDictionary<NSString*> *dict3 =
       [[GPBInt64ObjectDictionary alloc] initWithObjects:kObjects1
                                                 forKeys:kKeys2
                                                   count:GPBARRAYSIZE(kObjects1)];
   XCTAssertNotNil(dict3);
-  GPBInt64ObjectDictionary *dict4 =
+  GPBInt64ObjectDictionary<NSString*> *dict4 =
       [[GPBInt64ObjectDictionary alloc] initWithObjects:kObjects3
                                                 forKeys:kKeys1
                                                   count:GPBARRAYSIZE(kObjects3)];
@@ -3491,14 +3491,14 @@
 
 - (void)testCopy {
   const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
-  const id kObjects[] = { @"abc", @"def", @"ghi", @"jkl" };
-  GPBInt64ObjectDictionary *dict =
+  const NSString* kObjects[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBInt64ObjectDictionary<NSString*> *dict =
       [[GPBInt64ObjectDictionary alloc] initWithObjects:kObjects
                                                 forKeys:kKeys
                                                   count:GPBARRAYSIZE(kObjects)];
   XCTAssertNotNil(dict);
 
-  GPBInt64ObjectDictionary *dict2 = [dict copy];
+  GPBInt64ObjectDictionary<NSString*> *dict2 = [dict copy];
   XCTAssertNotNil(dict2);
 
   // Should be new object but equal.
@@ -3512,14 +3512,14 @@
 
 - (void)testDictionaryFromDictionary {
   const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
-  const id kObjects[] = { @"abc", @"def", @"ghi", @"jkl" };
-  GPBInt64ObjectDictionary *dict =
+  const NSString* kObjects[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBInt64ObjectDictionary<NSString*> *dict =
       [[GPBInt64ObjectDictionary alloc] initWithObjects:kObjects
                                                 forKeys:kKeys
                                                   count:GPBARRAYSIZE(kObjects)];
   XCTAssertNotNil(dict);
 
-  GPBInt64ObjectDictionary *dict2 =
+  GPBInt64ObjectDictionary<NSString*> *dict2 =
       [GPBInt64ObjectDictionary dictionaryWithDictionary:dict];
   XCTAssertNotNil(dict2);
 
@@ -3530,7 +3530,7 @@
 }
 
 - (void)testAdds {
-  GPBInt64ObjectDictionary *dict = [GPBInt64ObjectDictionary dictionary];
+  GPBInt64ObjectDictionary<NSString*> *dict = [GPBInt64ObjectDictionary dictionary];
   XCTAssertNotNil(dict);
 
   XCTAssertEqual(dict.count, 0U);
@@ -3538,8 +3538,8 @@
   XCTAssertEqual(dict.count, 1U);
 
   const int64_t kKeys[] = { 22LL, 23LL, 24LL };
-  const id kObjects[] = { @"def", @"ghi", @"jkl" };
-  GPBInt64ObjectDictionary *dict2 =
+  const NSString* kObjects[] = { @"def", @"ghi", @"jkl" };
+  GPBInt64ObjectDictionary<NSString*> *dict2 =
       [[GPBInt64ObjectDictionary alloc] initWithObjects:kObjects
                                                 forKeys:kKeys
                                                   count:GPBARRAYSIZE(kObjects)];
@@ -3556,8 +3556,8 @@
 
 - (void)testRemove {
   const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
-  const id kObjects[] = { @"abc", @"def", @"ghi", @"jkl" };
-  GPBInt64ObjectDictionary *dict =
+  const NSString* kObjects[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBInt64ObjectDictionary<NSString*> *dict =
       [[GPBInt64ObjectDictionary alloc] initWithObjects:kObjects
                                          forKeys:kKeys
                                            count:GPBARRAYSIZE(kObjects)];
@@ -3597,8 +3597,8 @@
 
 - (void)testInplaceMutation {
   const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
-  const id kObjects[] = { @"abc", @"def", @"ghi", @"jkl" };
-  GPBInt64ObjectDictionary *dict =
+  const NSString* kObjects[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBInt64ObjectDictionary<NSString*> *dict =
       [[GPBInt64ObjectDictionary alloc] initWithObjects:kObjects
                                          forKeys:kKeys
                                            count:GPBARRAYSIZE(kObjects)];
@@ -3624,8 +3624,8 @@
   XCTAssertEqualObjects([dict objectForKey:24LL], @"def");
 
   const int64_t kKeys2[] = { 22LL, 23LL };
-  const id kObjects2[] = { @"ghi", @"abc" };
-  GPBInt64ObjectDictionary *dict2 =
+  const NSString* kObjects2[] = { @"ghi", @"abc" };
+  GPBInt64ObjectDictionary<NSString*> *dict2 =
       [[GPBInt64ObjectDictionary alloc] initWithObjects:kObjects2
                                                 forKeys:kKeys2
                                                   count:GPBARRAYSIZE(kObjects2)];
diff --git a/objectivec/Tests/GPBDictionaryTests+UInt32.m b/objectivec/Tests/GPBDictionaryTests+UInt32.m
index c7c5765..499f2ad 100644
--- a/objectivec/Tests/GPBDictionaryTests+UInt32.m
+++ b/objectivec/Tests/GPBDictionaryTests+UInt32.m
@@ -3363,11 +3363,11 @@
 @implementation GPBUInt32ObjectDictionaryTests
 
 - (void)testEmpty {
-  GPBUInt32ObjectDictionary *dict = [[GPBUInt32ObjectDictionary alloc] init];
+  GPBUInt32ObjectDictionary<NSString*> *dict = [[GPBUInt32ObjectDictionary alloc] init];
   XCTAssertNotNil(dict);
   XCTAssertEqual(dict.count, 0U);
   XCTAssertNil([dict objectForKey:1U]);
-  [dict enumerateKeysAndObjectsUsingBlock:^(uint32_t aKey, id aObject, BOOL *stop) {
+  [dict enumerateKeysAndObjectsUsingBlock:^(uint32_t aKey, NSString* aObject, BOOL *stop) {
     #pragma unused(aKey, aObject, stop)
     XCTFail(@"Shouldn't get here!");
   }];
@@ -3375,12 +3375,12 @@
 }
 
 - (void)testOne {
-  GPBUInt32ObjectDictionary *dict = [GPBUInt32ObjectDictionary dictionaryWithObject:@"abc" forKey:1U];
+  GPBUInt32ObjectDictionary<NSString*> *dict = [GPBUInt32ObjectDictionary dictionaryWithObject:@"abc" forKey:1U];
   XCTAssertNotNil(dict);
   XCTAssertEqual(dict.count, 1U);
   XCTAssertEqualObjects([dict objectForKey:1U], @"abc");
   XCTAssertNil([dict objectForKey:2U]);
-  [dict enumerateKeysAndObjectsUsingBlock:^(uint32_t aKey, id aObject, BOOL *stop) {
+  [dict enumerateKeysAndObjectsUsingBlock:^(uint32_t aKey, NSString* aObject, BOOL *stop) {
     XCTAssertEqual(aKey, 1U);
     XCTAssertEqualObjects(aObject, @"abc");
     XCTAssertNotEqual(stop, NULL);
@@ -3389,8 +3389,8 @@
 
 - (void)testBasics {
   const uint32_t kKeys[] = { 1U, 2U, 3U };
-  const id kObjects[] = { @"abc", @"def", @"ghi" };
-  GPBUInt32ObjectDictionary *dict =
+  const NSString* kObjects[] = { @"abc", @"def", @"ghi" };
+  GPBUInt32ObjectDictionary<NSString*> *dict =
       [[GPBUInt32ObjectDictionary alloc] initWithObjects:kObjects
                                                  forKeys:kKeys
                                                    count:GPBARRAYSIZE(kObjects)];
@@ -3403,8 +3403,8 @@
 
   __block NSUInteger idx = 0;
   uint32_t *seenKeys = malloc(3 * sizeof(uint32_t));
-  id *seenObjects = malloc(3 * sizeof(id));
-  [dict enumerateKeysAndObjectsUsingBlock:^(uint32_t aKey, id aObject, BOOL *stop) {
+  NSString* *seenObjects = malloc(3 * sizeof(NSString*));
+  [dict enumerateKeysAndObjectsUsingBlock:^(uint32_t aKey, NSString* aObject, BOOL *stop) {
     XCTAssertLessThan(idx, 3U);
     seenKeys[idx] = aKey;
     seenObjects[idx] = aObject;
@@ -3426,7 +3426,7 @@
 
   // Stopping the enumeration.
   idx = 0;
-  [dict enumerateKeysAndObjectsUsingBlock:^(uint32_t aKey, id aObject, BOOL *stop) {
+  [dict enumerateKeysAndObjectsUsingBlock:^(uint32_t aKey, NSString* aObject, BOOL *stop) {
     #pragma unused(aKey, aObject)
     if (idx == 1) *stop = YES;
     XCTAssertNotEqual(idx, 2U);
@@ -3438,30 +3438,30 @@
 - (void)testEquality {
   const uint32_t kKeys1[] = { 1U, 2U, 3U, 4U };
   const uint32_t kKeys2[] = { 2U, 1U, 4U };
-  const id kObjects1[] = { @"abc", @"def", @"ghi" };
-  const id kObjects2[] = { @"abc", @"jkl", @"ghi" };
-  const id kObjects3[] = { @"abc", @"def", @"ghi", @"jkl" };
-  GPBUInt32ObjectDictionary *dict1 =
+  const NSString* kObjects1[] = { @"abc", @"def", @"ghi" };
+  const NSString* kObjects2[] = { @"abc", @"jkl", @"ghi" };
+  const NSString* kObjects3[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBUInt32ObjectDictionary<NSString*> *dict1 =
       [[GPBUInt32ObjectDictionary alloc] initWithObjects:kObjects1
                                                  forKeys:kKeys1
                                                    count:GPBARRAYSIZE(kObjects1)];
   XCTAssertNotNil(dict1);
-  GPBUInt32ObjectDictionary *dict1prime =
+  GPBUInt32ObjectDictionary<NSString*> *dict1prime =
       [[GPBUInt32ObjectDictionary alloc] initWithObjects:kObjects1
                                                  forKeys:kKeys1
                                                    count:GPBARRAYSIZE(kObjects1)];
   XCTAssertNotNil(dict1prime);
-  GPBUInt32ObjectDictionary *dict2 =
+  GPBUInt32ObjectDictionary<NSString*> *dict2 =
       [[GPBUInt32ObjectDictionary alloc] initWithObjects:kObjects2
                                                  forKeys:kKeys1
                                                    count:GPBARRAYSIZE(kObjects2)];
   XCTAssertNotNil(dict2);
-  GPBUInt32ObjectDictionary *dict3 =
+  GPBUInt32ObjectDictionary<NSString*> *dict3 =
       [[GPBUInt32ObjectDictionary alloc] initWithObjects:kObjects1
                                                  forKeys:kKeys2
                                                    count:GPBARRAYSIZE(kObjects1)];
   XCTAssertNotNil(dict3);
-  GPBUInt32ObjectDictionary *dict4 =
+  GPBUInt32ObjectDictionary<NSString*> *dict4 =
       [[GPBUInt32ObjectDictionary alloc] initWithObjects:kObjects3
                                                  forKeys:kKeys1
                                                    count:GPBARRAYSIZE(kObjects3)];
@@ -3491,14 +3491,14 @@
 
 - (void)testCopy {
   const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
-  const id kObjects[] = { @"abc", @"def", @"ghi", @"jkl" };
-  GPBUInt32ObjectDictionary *dict =
+  const NSString* kObjects[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBUInt32ObjectDictionary<NSString*> *dict =
       [[GPBUInt32ObjectDictionary alloc] initWithObjects:kObjects
                                                  forKeys:kKeys
                                                    count:GPBARRAYSIZE(kObjects)];
   XCTAssertNotNil(dict);
 
-  GPBUInt32ObjectDictionary *dict2 = [dict copy];
+  GPBUInt32ObjectDictionary<NSString*> *dict2 = [dict copy];
   XCTAssertNotNil(dict2);
 
   // Should be new object but equal.
@@ -3512,14 +3512,14 @@
 
 - (void)testDictionaryFromDictionary {
   const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
-  const id kObjects[] = { @"abc", @"def", @"ghi", @"jkl" };
-  GPBUInt32ObjectDictionary *dict =
+  const NSString* kObjects[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBUInt32ObjectDictionary<NSString*> *dict =
       [[GPBUInt32ObjectDictionary alloc] initWithObjects:kObjects
                                                  forKeys:kKeys
                                                    count:GPBARRAYSIZE(kObjects)];
   XCTAssertNotNil(dict);
 
-  GPBUInt32ObjectDictionary *dict2 =
+  GPBUInt32ObjectDictionary<NSString*> *dict2 =
       [GPBUInt32ObjectDictionary dictionaryWithDictionary:dict];
   XCTAssertNotNil(dict2);
 
@@ -3530,7 +3530,7 @@
 }
 
 - (void)testAdds {
-  GPBUInt32ObjectDictionary *dict = [GPBUInt32ObjectDictionary dictionary];
+  GPBUInt32ObjectDictionary<NSString*> *dict = [GPBUInt32ObjectDictionary dictionary];
   XCTAssertNotNil(dict);
 
   XCTAssertEqual(dict.count, 0U);
@@ -3538,8 +3538,8 @@
   XCTAssertEqual(dict.count, 1U);
 
   const uint32_t kKeys[] = { 2U, 3U, 4U };
-  const id kObjects[] = { @"def", @"ghi", @"jkl" };
-  GPBUInt32ObjectDictionary *dict2 =
+  const NSString* kObjects[] = { @"def", @"ghi", @"jkl" };
+  GPBUInt32ObjectDictionary<NSString*> *dict2 =
       [[GPBUInt32ObjectDictionary alloc] initWithObjects:kObjects
                                                  forKeys:kKeys
                                                    count:GPBARRAYSIZE(kObjects)];
@@ -3556,8 +3556,8 @@
 
 - (void)testRemove {
   const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
-  const id kObjects[] = { @"abc", @"def", @"ghi", @"jkl" };
-  GPBUInt32ObjectDictionary *dict =
+  const NSString* kObjects[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBUInt32ObjectDictionary<NSString*> *dict =
       [[GPBUInt32ObjectDictionary alloc] initWithObjects:kObjects
                                           forKeys:kKeys
                                             count:GPBARRAYSIZE(kObjects)];
@@ -3597,8 +3597,8 @@
 
 - (void)testInplaceMutation {
   const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
-  const id kObjects[] = { @"abc", @"def", @"ghi", @"jkl" };
-  GPBUInt32ObjectDictionary *dict =
+  const NSString* kObjects[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBUInt32ObjectDictionary<NSString*> *dict =
       [[GPBUInt32ObjectDictionary alloc] initWithObjects:kObjects
                                           forKeys:kKeys
                                             count:GPBARRAYSIZE(kObjects)];
@@ -3624,8 +3624,8 @@
   XCTAssertEqualObjects([dict objectForKey:4U], @"def");
 
   const uint32_t kKeys2[] = { 2U, 3U };
-  const id kObjects2[] = { @"ghi", @"abc" };
-  GPBUInt32ObjectDictionary *dict2 =
+  const NSString* kObjects2[] = { @"ghi", @"abc" };
+  GPBUInt32ObjectDictionary<NSString*> *dict2 =
       [[GPBUInt32ObjectDictionary alloc] initWithObjects:kObjects2
                                                  forKeys:kKeys2
                                                    count:GPBARRAYSIZE(kObjects2)];
diff --git a/objectivec/Tests/GPBDictionaryTests+UInt64.m b/objectivec/Tests/GPBDictionaryTests+UInt64.m
index b64d3a9..327e154 100644
--- a/objectivec/Tests/GPBDictionaryTests+UInt64.m
+++ b/objectivec/Tests/GPBDictionaryTests+UInt64.m
@@ -3363,11 +3363,11 @@
 @implementation GPBUInt64ObjectDictionaryTests
 
 - (void)testEmpty {
-  GPBUInt64ObjectDictionary *dict = [[GPBUInt64ObjectDictionary alloc] init];
+  GPBUInt64ObjectDictionary<NSString*> *dict = [[GPBUInt64ObjectDictionary alloc] init];
   XCTAssertNotNil(dict);
   XCTAssertEqual(dict.count, 0U);
   XCTAssertNil([dict objectForKey:31ULL]);
-  [dict enumerateKeysAndObjectsUsingBlock:^(uint64_t aKey, id aObject, BOOL *stop) {
+  [dict enumerateKeysAndObjectsUsingBlock:^(uint64_t aKey, NSString* aObject, BOOL *stop) {
     #pragma unused(aKey, aObject, stop)
     XCTFail(@"Shouldn't get here!");
   }];
@@ -3375,12 +3375,12 @@
 }
 
 - (void)testOne {
-  GPBUInt64ObjectDictionary *dict = [GPBUInt64ObjectDictionary dictionaryWithObject:@"abc" forKey:31ULL];
+  GPBUInt64ObjectDictionary<NSString*> *dict = [GPBUInt64ObjectDictionary dictionaryWithObject:@"abc" forKey:31ULL];
   XCTAssertNotNil(dict);
   XCTAssertEqual(dict.count, 1U);
   XCTAssertEqualObjects([dict objectForKey:31ULL], @"abc");
   XCTAssertNil([dict objectForKey:32ULL]);
-  [dict enumerateKeysAndObjectsUsingBlock:^(uint64_t aKey, id aObject, BOOL *stop) {
+  [dict enumerateKeysAndObjectsUsingBlock:^(uint64_t aKey, NSString* aObject, BOOL *stop) {
     XCTAssertEqual(aKey, 31ULL);
     XCTAssertEqualObjects(aObject, @"abc");
     XCTAssertNotEqual(stop, NULL);
@@ -3389,8 +3389,8 @@
 
 - (void)testBasics {
   const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL };
-  const id kObjects[] = { @"abc", @"def", @"ghi" };
-  GPBUInt64ObjectDictionary *dict =
+  const NSString* kObjects[] = { @"abc", @"def", @"ghi" };
+  GPBUInt64ObjectDictionary<NSString*> *dict =
       [[GPBUInt64ObjectDictionary alloc] initWithObjects:kObjects
                                                  forKeys:kKeys
                                                    count:GPBARRAYSIZE(kObjects)];
@@ -3403,8 +3403,8 @@
 
   __block NSUInteger idx = 0;
   uint64_t *seenKeys = malloc(3 * sizeof(uint64_t));
-  id *seenObjects = malloc(3 * sizeof(id));
-  [dict enumerateKeysAndObjectsUsingBlock:^(uint64_t aKey, id aObject, BOOL *stop) {
+  NSString* *seenObjects = malloc(3 * sizeof(NSString*));
+  [dict enumerateKeysAndObjectsUsingBlock:^(uint64_t aKey, NSString* aObject, BOOL *stop) {
     XCTAssertLessThan(idx, 3U);
     seenKeys[idx] = aKey;
     seenObjects[idx] = aObject;
@@ -3426,7 +3426,7 @@
 
   // Stopping the enumeration.
   idx = 0;
-  [dict enumerateKeysAndObjectsUsingBlock:^(uint64_t aKey, id aObject, BOOL *stop) {
+  [dict enumerateKeysAndObjectsUsingBlock:^(uint64_t aKey, NSString* aObject, BOOL *stop) {
     #pragma unused(aKey, aObject)
     if (idx == 1) *stop = YES;
     XCTAssertNotEqual(idx, 2U);
@@ -3438,30 +3438,30 @@
 - (void)testEquality {
   const uint64_t kKeys1[] = { 31ULL, 32ULL, 33ULL, 34ULL };
   const uint64_t kKeys2[] = { 32ULL, 31ULL, 34ULL };
-  const id kObjects1[] = { @"abc", @"def", @"ghi" };
-  const id kObjects2[] = { @"abc", @"jkl", @"ghi" };
-  const id kObjects3[] = { @"abc", @"def", @"ghi", @"jkl" };
-  GPBUInt64ObjectDictionary *dict1 =
+  const NSString* kObjects1[] = { @"abc", @"def", @"ghi" };
+  const NSString* kObjects2[] = { @"abc", @"jkl", @"ghi" };
+  const NSString* kObjects3[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBUInt64ObjectDictionary<NSString*> *dict1 =
       [[GPBUInt64ObjectDictionary alloc] initWithObjects:kObjects1
                                                  forKeys:kKeys1
                                                    count:GPBARRAYSIZE(kObjects1)];
   XCTAssertNotNil(dict1);
-  GPBUInt64ObjectDictionary *dict1prime =
+  GPBUInt64ObjectDictionary<NSString*> *dict1prime =
       [[GPBUInt64ObjectDictionary alloc] initWithObjects:kObjects1
                                                  forKeys:kKeys1
                                                    count:GPBARRAYSIZE(kObjects1)];
   XCTAssertNotNil(dict1prime);
-  GPBUInt64ObjectDictionary *dict2 =
+  GPBUInt64ObjectDictionary<NSString*> *dict2 =
       [[GPBUInt64ObjectDictionary alloc] initWithObjects:kObjects2
                                                  forKeys:kKeys1
                                                    count:GPBARRAYSIZE(kObjects2)];
   XCTAssertNotNil(dict2);
-  GPBUInt64ObjectDictionary *dict3 =
+  GPBUInt64ObjectDictionary<NSString*> *dict3 =
       [[GPBUInt64ObjectDictionary alloc] initWithObjects:kObjects1
                                                  forKeys:kKeys2
                                                    count:GPBARRAYSIZE(kObjects1)];
   XCTAssertNotNil(dict3);
-  GPBUInt64ObjectDictionary *dict4 =
+  GPBUInt64ObjectDictionary<NSString*> *dict4 =
       [[GPBUInt64ObjectDictionary alloc] initWithObjects:kObjects3
                                                  forKeys:kKeys1
                                                    count:GPBARRAYSIZE(kObjects3)];
@@ -3491,14 +3491,14 @@
 
 - (void)testCopy {
   const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
-  const id kObjects[] = { @"abc", @"def", @"ghi", @"jkl" };
-  GPBUInt64ObjectDictionary *dict =
+  const NSString* kObjects[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBUInt64ObjectDictionary<NSString*> *dict =
       [[GPBUInt64ObjectDictionary alloc] initWithObjects:kObjects
                                                  forKeys:kKeys
                                                    count:GPBARRAYSIZE(kObjects)];
   XCTAssertNotNil(dict);
 
-  GPBUInt64ObjectDictionary *dict2 = [dict copy];
+  GPBUInt64ObjectDictionary<NSString*> *dict2 = [dict copy];
   XCTAssertNotNil(dict2);
 
   // Should be new object but equal.
@@ -3512,14 +3512,14 @@
 
 - (void)testDictionaryFromDictionary {
   const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
-  const id kObjects[] = { @"abc", @"def", @"ghi", @"jkl" };
-  GPBUInt64ObjectDictionary *dict =
+  const NSString* kObjects[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBUInt64ObjectDictionary<NSString*> *dict =
       [[GPBUInt64ObjectDictionary alloc] initWithObjects:kObjects
                                                  forKeys:kKeys
                                                    count:GPBARRAYSIZE(kObjects)];
   XCTAssertNotNil(dict);
 
-  GPBUInt64ObjectDictionary *dict2 =
+  GPBUInt64ObjectDictionary<NSString*> *dict2 =
       [GPBUInt64ObjectDictionary dictionaryWithDictionary:dict];
   XCTAssertNotNil(dict2);
 
@@ -3530,7 +3530,7 @@
 }
 
 - (void)testAdds {
-  GPBUInt64ObjectDictionary *dict = [GPBUInt64ObjectDictionary dictionary];
+  GPBUInt64ObjectDictionary<NSString*> *dict = [GPBUInt64ObjectDictionary dictionary];
   XCTAssertNotNil(dict);
 
   XCTAssertEqual(dict.count, 0U);
@@ -3538,8 +3538,8 @@
   XCTAssertEqual(dict.count, 1U);
 
   const uint64_t kKeys[] = { 32ULL, 33ULL, 34ULL };
-  const id kObjects[] = { @"def", @"ghi", @"jkl" };
-  GPBUInt64ObjectDictionary *dict2 =
+  const NSString* kObjects[] = { @"def", @"ghi", @"jkl" };
+  GPBUInt64ObjectDictionary<NSString*> *dict2 =
       [[GPBUInt64ObjectDictionary alloc] initWithObjects:kObjects
                                                  forKeys:kKeys
                                                    count:GPBARRAYSIZE(kObjects)];
@@ -3556,8 +3556,8 @@
 
 - (void)testRemove {
   const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
-  const id kObjects[] = { @"abc", @"def", @"ghi", @"jkl" };
-  GPBUInt64ObjectDictionary *dict =
+  const NSString* kObjects[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBUInt64ObjectDictionary<NSString*> *dict =
       [[GPBUInt64ObjectDictionary alloc] initWithObjects:kObjects
                                           forKeys:kKeys
                                             count:GPBARRAYSIZE(kObjects)];
@@ -3597,8 +3597,8 @@
 
 - (void)testInplaceMutation {
   const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
-  const id kObjects[] = { @"abc", @"def", @"ghi", @"jkl" };
-  GPBUInt64ObjectDictionary *dict =
+  const NSString* kObjects[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBUInt64ObjectDictionary<NSString*> *dict =
       [[GPBUInt64ObjectDictionary alloc] initWithObjects:kObjects
                                           forKeys:kKeys
                                             count:GPBARRAYSIZE(kObjects)];
@@ -3624,8 +3624,8 @@
   XCTAssertEqualObjects([dict objectForKey:34ULL], @"def");
 
   const uint64_t kKeys2[] = { 32ULL, 33ULL };
-  const id kObjects2[] = { @"ghi", @"abc" };
-  GPBUInt64ObjectDictionary *dict2 =
+  const NSString* kObjects2[] = { @"ghi", @"abc" };
+  GPBUInt64ObjectDictionary<NSString*> *dict2 =
       [[GPBUInt64ObjectDictionary alloc] initWithObjects:kObjects2
                                                  forKeys:kKeys2
                                                    count:GPBARRAYSIZE(kObjects2)];
diff --git a/objectivec/Tests/GPBDictionaryTests.pddm b/objectivec/Tests/GPBDictionaryTests.pddm
index ada93c6..0951294 100644
--- a/objectivec/Tests/GPBDictionaryTests.pddm
+++ b/objectivec/Tests/GPBDictionaryTests.pddm
@@ -30,7 +30,7 @@
 
 //%PDDM-DEFINE TEST_FOR_POD_KEY(KEY_NAME, KEY_TYPE, KEY1, KEY2, KEY3, KEY4)
 //%TESTS_FOR_POD_VALUES(KEY_NAME, KEY_TYPE, , , KEY1, KEY2, KEY3, KEY4)
-//%TESTS_FOR_POD_KEY_OBJECT_VALUE(KEY_NAME, KEY_TYPE, KEY1, KEY2, KEY3, KEY4, Object, id, @"abc", @"def", @"ghi", @"jkl")
+//%TESTS_FOR_POD_KEY_OBJECT_VALUE(KEY_NAME, KEY_TYPE, KEY1, KEY2, KEY3, KEY4, Object, NSString*, @"abc", @"def", @"ghi", @"jkl")
 
 //%PDDM-DEFINE TESTS_FOR_POD_VALUES(KEY_NAME, KEY_TYPE, KisP, KSUFFIX, KEY1, KEY2, KEY3, KEY4)
 //%TEST_HELPERS(KEY_NAME, KEY_TYPE, KisP)
@@ -50,6 +50,13 @@
 //%PDDM-DEFINE TESTS_FOR_POD_KEY_OBJECT_VALUE(KEY_NAME, KEY_TYPE, KEY1, KEY2, KEY3, KEY4, VALUE_NAME, VALUE_TYPE, VAL1, VAL2, VAL3, VAL4)
 //%TESTS_COMMON(KEY_NAME, KEY_TYPE, , , KEY1, KEY2, KEY3, KEY4, VALUE_NAME, VALUE_TYPE, Objects, object, OBJECT, , VAL1, VAL2, VAL3, VAL4)
 
+//%PDDM-DEFINE DICTIONARY_CLASS_DECLPOD(KEY_NAME, VALUE_NAME, VALUE_TYPE)
+//%GPB##KEY_NAME##VALUE_NAME##Dictionary
+//%PDDM-DEFINE DICTIONARY_CLASS_DECLEnum(KEY_NAME, VALUE_NAME, VALUE_TYPE)
+//%GPB##KEY_NAME##VALUE_NAME##Dictionary
+//%PDDM-DEFINE DICTIONARY_CLASS_DECLOBJECT(KEY_NAME, VALUE_NAME, VALUE_TYPE)
+//%GPB##KEY_NAME##VALUE_NAME##Dictionary<VALUE_TYPE>
+
 //%PDDM-DEFINE TESTS_COMMON(KEY_NAME, KEY_TYPE, KisP, KSUFFIX, KEY1, KEY2, KEY3, KEY4, VALUE_NAME, VALUE_TYPE, VSUFFIX, VNAME, VHELPER, VACCESSOR, VAL1, VAL2, VAL3, VAL4)
 //%#pragma mark - KEY_NAME -> VALUE_NAME
 //%
@@ -59,7 +66,7 @@
 //%@implementation GPB##KEY_NAME##VALUE_NAME##DictionaryTests
 //%
 //%- (void)testEmpty {
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict = [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] init];
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict = [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] init];
 //%  XCTAssertNotNil(dict);
 //%  XCTAssertEqual(dict.count, 0U);
 //%VALUE_NOT_FOUND##VHELPER(dict, KEY1)
@@ -71,7 +78,7 @@
 //%}
 //%
 //%- (void)testOne {
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict = [GPB##KEY_NAME##VALUE_NAME##Dictionary dictionaryWith##VNAME$u##:VAL1 forKey:KEY1];
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict = [GPB##KEY_NAME##VALUE_NAME##Dictionary dictionaryWith##VNAME$u##:VAL1 forKey:KEY1];
 //%  XCTAssertNotNil(dict);
 //%  XCTAssertEqual(dict.count, 1U);
 //%DECLARE_VALUE_STORAGE##VHELPER(VALUE_TYPE, VNAME)TEST_VALUE##VHELPER(dict, VNAME, KEY1, VAL1)
@@ -86,7 +93,7 @@
 //%- (void)testBasics {
 //%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY2, KEY3 };
 //%  const VALUE_TYPE k##VNAME$u##s[] = { VAL1, VAL2, VAL3 };
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWith##VNAME$u##s:k##VNAME$u##s
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##  forKeys:kKeys
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##    count:GPBARRAYSIZE(k##VNAME$u##s)];
@@ -137,27 +144,27 @@
 //%  const VALUE_TYPE k##VNAME$u##s1[] = { VAL1, VAL2, VAL3 };
 //%  const VALUE_TYPE k##VNAME$u##s2[] = { VAL1, VAL4, VAL3 };
 //%  const VALUE_TYPE k##VNAME$u##s3[] = { VAL1, VAL2, VAL3, VAL4 };
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict1 =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict1 =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWith##VNAME$u##s:k##VNAME$u##s1
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##  forKeys:kKeys1
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##    count:GPBARRAYSIZE(k##VNAME$u##s1)];
 //%  XCTAssertNotNil(dict1);
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict1prime =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict1prime =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWith##VNAME$u##s:k##VNAME$u##s1
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##  forKeys:kKeys1
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##    count:GPBARRAYSIZE(k##VNAME$u##s1)];
 //%  XCTAssertNotNil(dict1prime);
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict2 =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict2 =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWith##VNAME$u##s:k##VNAME$u##s2
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##  forKeys:kKeys1
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##    count:GPBARRAYSIZE(k##VNAME$u##s2)];
 //%  XCTAssertNotNil(dict2);
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict3 =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict3 =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWith##VNAME$u##s:k##VNAME$u##s1
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##  forKeys:kKeys2
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##    count:GPBARRAYSIZE(k##VNAME$u##s1)];
 //%  XCTAssertNotNil(dict3);
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict4 =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict4 =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWith##VNAME$u##s:k##VNAME$u##s3
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##  forKeys:kKeys1
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##    count:GPBARRAYSIZE(k##VNAME$u##s3)];
@@ -188,13 +195,13 @@
 //%- (void)testCopy {
 //%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY2, KEY3, KEY4 };
 //%  const VALUE_TYPE k##VNAME$u##s[] = { VAL1, VAL2, VAL3, VAL4 };
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWith##VNAME$u##s:k##VNAME$u##s
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##  forKeys:kKeys
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##    count:GPBARRAYSIZE(k##VNAME$u##s)];
 //%  XCTAssertNotNil(dict);
 //%
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict2 = [dict copy];
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict2 = [dict copy];
 //%  XCTAssertNotNil(dict2);
 //%
 //%  // Should be new object but equal.
@@ -209,13 +216,13 @@
 //%- (void)testDictionaryFromDictionary {
 //%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY2, KEY3, KEY4 };
 //%  const VALUE_TYPE k##VNAME$u##s[] = { VAL1, VAL2, VAL3, VAL4 };
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWith##VNAME$u##s:k##VNAME$u##s
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##  forKeys:kKeys
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##    count:GPBARRAYSIZE(k##VNAME$u##s)];
 //%  XCTAssertNotNil(dict);
 //%
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict2 =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict2 =
 //%      [GPB##KEY_NAME##VALUE_NAME##Dictionary dictionaryWithDictionary:dict];
 //%  XCTAssertNotNil(dict2);
 //%
@@ -226,7 +233,7 @@
 //%}
 //%
 //%- (void)testAdds {
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict = [GPB##KEY_NAME##VALUE_NAME##Dictionary dictionary];
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict = [GPB##KEY_NAME##VALUE_NAME##Dictionary dictionary];
 //%  XCTAssertNotNil(dict);
 //%
 //%  XCTAssertEqual(dict.count, 0U);
@@ -235,7 +242,7 @@
 //%
 //%  const KEY_TYPE KisP##kKeys[] = { KEY2, KEY3, KEY4 };
 //%  const VALUE_TYPE k##VNAME$u##s[] = { VAL2, VAL3, VAL4 };
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict2 =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict2 =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWith##VNAME$u##s:k##VNAME$u##s
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##  forKeys:kKeys
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##    count:GPBARRAYSIZE(k##VNAME$u##s)];
@@ -253,7 +260,7 @@
 //%- (void)testRemove {
 //%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY2, KEY3, KEY4 };
 //%  const VALUE_TYPE k##VNAME$u##s[] = { VAL1, VAL2, VAL3, VAL4 };
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWith##VNAME$u##s:k##VNAME$u##s
 //%           KEY_NAME$S VALUE_NAME$S          ##VNAME$S##  forKeys:kKeys
 //%           KEY_NAME$S VALUE_NAME$S          ##VNAME$S##    count:GPBARRAYSIZE(k##VNAME$u##s)];
@@ -294,7 +301,7 @@
 //%- (void)testInplaceMutation {
 //%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY2, KEY3, KEY4 };
 //%  const VALUE_TYPE k##VNAME$u##s[] = { VAL1, VAL2, VAL3, VAL4 };
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWith##VNAME$u##s:k##VNAME$u##s
 //%           KEY_NAME$S VALUE_NAME$S          ##VNAME$S##  forKeys:kKeys
 //%           KEY_NAME$S VALUE_NAME$S          ##VNAME$S##    count:GPBARRAYSIZE(k##VNAME$u##s)];
@@ -321,7 +328,7 @@
 //%
 //%  const KEY_TYPE KisP##kKeys2[] = { KEY2, KEY3 };
 //%  const VALUE_TYPE k##VNAME$u##s2[] = { VAL3, VAL1 };
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict2 =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict2 =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWith##VNAME$u##s:k##VNAME$u##s2
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##  forKeys:kKeys2
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##    count:GPBARRAYSIZE(k##VNAME$u##s2)];
@@ -353,7 +360,7 @@
 //%- (void)testRawBasics {
 //%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY2, KEY3 };
 //%  const VALUE_TYPE kValues[] = { VAL1, VAL2, VAL3 };
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
 //%           KEY_NAME$S VALUE_NAME$S                                  rawValues:kValues
 //%           KEY_NAME$S VALUE_NAME$S                                    forKeys:kKeys
@@ -429,31 +436,31 @@
 //%  const VALUE_TYPE kValues1[] = { VAL1, VAL2, VAL3 };  // Unknown
 //%  const VALUE_TYPE kValues2[] = { VAL1, VAL4, VAL3 };  // Unknown
 //%  const VALUE_TYPE kValues3[] = { VAL1, VAL2, VAL3, VAL4 };  // Unknowns
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict1 =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict1 =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
 //%           KEY_NAME$S VALUE_NAME$S                                  rawValues:kValues1
 //%           KEY_NAME$S VALUE_NAME$S                                    forKeys:kKeys1
 //%           KEY_NAME$S VALUE_NAME$S                                      count:GPBARRAYSIZE(kValues1)];
 //%  XCTAssertNotNil(dict1);
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict1prime =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict1prime =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
 //%           KEY_NAME$S VALUE_NAME$S                                  rawValues:kValues1
 //%           KEY_NAME$S VALUE_NAME$S                                    forKeys:kKeys1
 //%           KEY_NAME$S VALUE_NAME$S                                      count:GPBARRAYSIZE(kValues1)];
 //%  XCTAssertNotNil(dict1prime);
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict2 =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict2 =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
 //%           KEY_NAME$S VALUE_NAME$S                                  rawValues:kValues2
 //%           KEY_NAME$S VALUE_NAME$S                                    forKeys:kKeys1
 //%           KEY_NAME$S VALUE_NAME$S                                      count:GPBARRAYSIZE(kValues2)];
 //%  XCTAssertNotNil(dict2);
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict3 =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict3 =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
 //%           KEY_NAME$S VALUE_NAME$S                                  rawValues:kValues1
 //%           KEY_NAME$S VALUE_NAME$S                                    forKeys:kKeys2
 //%           KEY_NAME$S VALUE_NAME$S                                      count:GPBARRAYSIZE(kValues1)];
 //%  XCTAssertNotNil(dict3);
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict4 =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict4 =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
 //%           KEY_NAME$S VALUE_NAME$S                                  rawValues:kValues3
 //%           KEY_NAME$S VALUE_NAME$S                                    forKeys:kKeys1
@@ -485,14 +492,14 @@
 //%- (void)testCopyWithUnknowns {
 //%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY2, KEY3, KEY4 };
 //%  const VALUE_TYPE kValues[] = { VAL1, VAL2, VAL3, VAL4 };  // Unknown
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
 //%           KEY_NAME$S VALUE_NAME$S                                  rawValues:kValues
 //%           KEY_NAME$S VALUE_NAME$S                                    forKeys:kKeys
 //%           KEY_NAME$S VALUE_NAME$S                                      count:GPBARRAYSIZE(kValues)];
 //%  XCTAssertNotNil(dict);
 //%
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict2 = [dict copy];
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict2 = [dict copy];
 //%  XCTAssertNotNil(dict2);
 //%
 //%  // Should be new pointer, but equal objects.
@@ -507,14 +514,14 @@
 //%- (void)testDictionaryFromDictionary {
 //%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY2, KEY3, KEY4 };
 //%  const VALUE_TYPE kValues[] = { VAL1, VAL2, VAL3, VAL4 };  // Unknowns
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
 //%           KEY_NAME$S VALUE_NAME$S                                  rawValues:kValues
 //%           KEY_NAME$S VALUE_NAME$S                                    forKeys:kKeys
 //%           KEY_NAME$S VALUE_NAME$S                                      count:GPBARRAYSIZE(kValues)];
 //%  XCTAssertNotNil(dict);
 //%
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict2 =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict2 =
 //%      [GPB##KEY_NAME##VALUE_NAME##Dictionary dictionaryWithDictionary:dict];
 //%  XCTAssertNotNil(dict2);
 //%
@@ -526,7 +533,7 @@
 //%}
 //%
 //%- (void)testUnknownAdds {
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict =
 //%    [GPB##KEY_NAME##VALUE_NAME##Dictionary dictionaryWithValidationFunction:TestingEnum_IsValidValue];
 //%  XCTAssertNotNil(dict);
 //%
@@ -539,7 +546,7 @@
 //%
 //%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY3, KEY4 };
 //%  const VALUE_TYPE kValues[] = { VAL1, VAL3, VAL4 };  // Unknown
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict2 =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict2 =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValues:kValues
 //%           KEY_NAME$S VALUE_NAME$S                        forKeys:kKeys
 //%           KEY_NAME$S VALUE_NAME$S                          count:GPBARRAYSIZE(kValues)];
@@ -559,7 +566,7 @@
 //%- (void)testUnknownRemove {
 //%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY2, KEY3, KEY4 };
 //%  const VALUE_TYPE kValues[] = { VAL1, VAL2, VAL3, VAL4 };  // Unknowns
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
 //%           KEY_NAME$S VALUE_NAME$S                                  rawValues:kValues
 //%           KEY_NAME$S VALUE_NAME$S                                    forKeys:kKeys
@@ -601,7 +608,7 @@
 //%- (void)testInplaceMutationUnknowns {
 //%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY2, KEY3, KEY4 };
 //%  const VALUE_TYPE kValues[] = { VAL1, VAL2, VAL3, VAL4 };  // Unknowns
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
 //%           KEY_NAME$S VALUE_NAME$S                                  rawValues:kValues
 //%           KEY_NAME$S VALUE_NAME$S                                    forKeys:kKeys
@@ -637,7 +644,7 @@
 //%
 //%  const KEY_TYPE KisP##kKeys2[] = { KEY2, KEY3 };
 //%  const VALUE_TYPE kValues2[] = { VAL3, VAL2 };  // Unknown
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict2 =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict2 =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
 //%           KEY_NAME$S VALUE_NAME$S                                  rawValues:kValues2
 //%           KEY_NAME$S VALUE_NAME$S                                    forKeys:kKeys2
@@ -657,14 +664,14 @@
 //%- (void)testCopyUnknowns {
 //%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY2, KEY3, KEY4 };
 //%  const VALUE_TYPE kValues[] = { VAL1, VAL2, VAL3, VAL4 };
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
 //%           KEY_NAME$S VALUE_NAME$S                                  rawValues:kValues
 //%           KEY_NAME$S VALUE_NAME$S                                    forKeys:kKeys
 //%           KEY_NAME$S VALUE_NAME$S                                      count:GPBARRAYSIZE(kValues)];
 //%  XCTAssertNotNil(dict);
 //%
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict2 = [dict copy];
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict2 = [dict copy];
 //%  XCTAssertNotNil(dict2);
 //%
 //%  // Should be new pointer, but equal objects.
@@ -782,7 +789,7 @@
 //%@implementation GPB##KEY_NAME##VALUE_NAME##DictionaryTests
 //%
 //%- (void)testEmpty {
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict = [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] init];
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict = [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] init];
 //%  XCTAssertNotNil(dict);
 //%  XCTAssertEqual(dict.count, 0U);
 //%VALUE_NOT_FOUND##VHELPER(dict, KEY1)
@@ -794,7 +801,7 @@
 //%}
 //%
 //%- (void)testOne {
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict = [GPB##KEY_NAME##VALUE_NAME##Dictionary dictionaryWith##VNAME$u##:VAL1 forKey:KEY1];
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict = [GPB##KEY_NAME##VALUE_NAME##Dictionary dictionaryWith##VNAME$u##:VAL1 forKey:KEY1];
 //%  XCTAssertNotNil(dict);
 //%  XCTAssertEqual(dict.count, 1U);
 //%DECLARE_VALUE_STORAGE##VHELPER(VALUE_TYPE, VNAME)TEST_VALUE##VHELPER(dict, VNAME, KEY1, VAL1)
@@ -809,7 +816,7 @@
 //%- (void)testBasics {
 //%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY2 };
 //%  const VALUE_TYPE k##VNAME$u##s[] = { VAL1, VAL2 };
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWith##VNAME$u##s:k##VNAME$u##s
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##  forKeys:kKeys
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##    count:GPBARRAYSIZE(k##VNAME$u##s)];
@@ -858,27 +865,27 @@
 //%  const VALUE_TYPE k##VNAME$u##s1[] = { VAL1, VAL2 };
 //%  const VALUE_TYPE k##VNAME$u##s2[] = { VAL2, VAL1 };
 //%  const VALUE_TYPE k##VNAME$u##s3[] = { VAL2 };
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict1 =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict1 =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWith##VNAME$u##s:k##VNAME$u##s1
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##  forKeys:kKeys1
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##    count:GPBARRAYSIZE(k##VNAME$u##s1)];
 //%  XCTAssertNotNil(dict1);
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict1prime =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict1prime =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWith##VNAME$u##s:k##VNAME$u##s1
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##  forKeys:kKeys1
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##    count:GPBARRAYSIZE(k##VNAME$u##s1)];
 //%  XCTAssertNotNil(dict1prime);
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict2 =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict2 =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWith##VNAME$u##s:k##VNAME$u##s2
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##  forKeys:kKeys1
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##    count:GPBARRAYSIZE(k##VNAME$u##s2)];
 //%  XCTAssertNotNil(dict2);
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict3 =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict3 =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWith##VNAME$u##s:k##VNAME$u##s1
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##  forKeys:kKeys2
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##    count:GPBARRAYSIZE(k##VNAME$u##s1)];
 //%  XCTAssertNotNil(dict3);
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict4 =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict4 =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWith##VNAME$u##s:k##VNAME$u##s3
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##  forKeys:kKeys1
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##    count:GPBARRAYSIZE(k##VNAME$u##s3)];
@@ -909,13 +916,13 @@
 //%- (void)testCopy {
 //%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY2 };
 //%  const VALUE_TYPE k##VNAME$u##s[] = { VAL1, VAL2 };
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWith##VNAME$u##s:k##VNAME$u##s
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##  forKeys:kKeys
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##    count:GPBARRAYSIZE(k##VNAME$u##s)];
 //%  XCTAssertNotNil(dict);
 //%
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict2 = [dict copy];
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict2 = [dict copy];
 //%  XCTAssertNotNil(dict2);
 //%
 //%  // Should be new object but equal.
@@ -930,13 +937,13 @@
 //%- (void)testDictionaryFromDictionary {
 //%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY2 };
 //%  const VALUE_TYPE k##VNAME$u##s[] = { VAL1, VAL2 };
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWith##VNAME$u##s:k##VNAME$u##s
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##  forKeys:kKeys
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##    count:GPBARRAYSIZE(k##VNAME$u##s)];
 //%  XCTAssertNotNil(dict);
 //%
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict2 =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict2 =
 //%      [GPB##KEY_NAME##VALUE_NAME##Dictionary dictionaryWithDictionary:dict];
 //%  XCTAssertNotNil(dict2);
 //%
@@ -947,7 +954,7 @@
 //%}
 //%
 //%- (void)testAdds {
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict = [GPB##KEY_NAME##VALUE_NAME##Dictionary dictionary];
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict = [GPB##KEY_NAME##VALUE_NAME##Dictionary dictionary];
 //%  XCTAssertNotNil(dict);
 //%
 //%  XCTAssertEqual(dict.count, 0U);
@@ -956,7 +963,7 @@
 //%
 //%  const KEY_TYPE KisP##kKeys[] = { KEY2 };
 //%  const VALUE_TYPE k##VNAME$u##s[] = { VAL2 };
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict2 =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict2 =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWith##VNAME$u##s:k##VNAME$u##s
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##  forKeys:kKeys
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##    count:GPBARRAYSIZE(k##VNAME$u##s)];
@@ -972,7 +979,7 @@
 //%- (void)testRemove {
 //%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY2};
 //%  const VALUE_TYPE k##VNAME$u##s[] = { VAL1, VAL2 };
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWith##VNAME$u##s:k##VNAME$u##s
 //%           KEY_NAME$S VALUE_NAME$S          ##VNAME$S##  forKeys:kKeys
 //%           KEY_NAME$S VALUE_NAME$S          ##VNAME$S##    count:GPBARRAYSIZE(k##VNAME$u##s)];
@@ -1000,7 +1007,7 @@
 //%- (void)testInplaceMutation {
 //%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY2 };
 //%  const VALUE_TYPE k##VNAME$u##s[] = { VAL1, VAL2 };
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWith##VNAME$u##s:k##VNAME$u##s
 //%           KEY_NAME$S VALUE_NAME$S          ##VNAME$S##  forKeys:kKeys
 //%           KEY_NAME$S VALUE_NAME$S          ##VNAME$S##    count:GPBARRAYSIZE(k##VNAME$u##s)];
@@ -1021,7 +1028,7 @@
 //%
 //%  const KEY_TYPE KisP##kKeys2[] = { KEY2, KEY1 };
 //%  const VALUE_TYPE k##VNAME$u##s2[] = { VAL2, VAL1 };
-//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict2 =
+//%  DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) *dict2 =
 //%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWith##VNAME$u##s:k##VNAME$u##s2
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##  forKeys:kKeys2
 //%           KEY_NAME$S VALUE_NAME$S                 ##VNAME$S##    count:GPBARRAYSIZE(k##VNAME$u##s2)];
diff --git a/objectivec/google/protobuf/Api.pbobjc.h b/objectivec/google/protobuf/Api.pbobjc.h
index c3cf8e9..8d82b15 100644
--- a/objectivec/google/protobuf/Api.pbobjc.h
+++ b/objectivec/google/protobuf/Api.pbobjc.h
@@ -11,6 +11,9 @@
 
 CF_EXTERN_C_BEGIN
 
+@class GPBMethod;
+@class GPBMixin;
+@class GPBOption;
 @class GPBSourceContext;
 GPB_ENUM_FWD_DECLARE(GPBSyntax);
 
@@ -47,13 +50,11 @@
 @property(nonatomic, readwrite, copy, null_resettable) NSString *name;
 
 // The methods of this api, in unspecified order.
-// |methodsArray| contains |GPBMethod|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *methodsArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBMethod*> *methodsArray;
 @property(nonatomic, readonly) NSUInteger methodsArray_Count;
 
 // Any metadata attached to the API.
-// |optionsArray| contains |GPBOption|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *optionsArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBOption*> *optionsArray;
 @property(nonatomic, readonly) NSUInteger optionsArray_Count;
 
 // A version string for this api. If specified, must have the form
@@ -84,8 +85,7 @@
 @property(nonatomic, readwrite, strong, null_resettable) GPBSourceContext *sourceContext;
 
 // Included APIs. See [Mixin][].
-// |mixinsArray| contains |GPBMixin|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *mixinsArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBMixin*> *mixinsArray;
 @property(nonatomic, readonly) NSUInteger mixinsArray_Count;
 
 // The source syntax of the service.
@@ -127,8 +127,7 @@
 @property(nonatomic, readwrite) BOOL responseStreaming;
 
 // Any metadata attached to the method.
-// |optionsArray| contains |GPBOption|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *optionsArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBOption*> *optionsArray;
 @property(nonatomic, readonly) NSUInteger optionsArray_Count;
 
 // The source syntax of this method.
diff --git a/objectivec/google/protobuf/Descriptor.pbobjc.h b/objectivec/google/protobuf/Descriptor.pbobjc.h
index 9c43cfd..2ab2024 100644
--- a/objectivec/google/protobuf/Descriptor.pbobjc.h
+++ b/objectivec/google/protobuf/Descriptor.pbobjc.h
@@ -11,14 +11,28 @@
 
 CF_EXTERN_C_BEGIN
 
+@class GPBDescriptorProto;
+@class GPBDescriptorProto_ExtensionRange;
+@class GPBDescriptorProto_ReservedRange;
+@class GPBEnumDescriptorProto;
 @class GPBEnumOptions;
+@class GPBEnumValueDescriptorProto;
 @class GPBEnumValueOptions;
+@class GPBFieldDescriptorProto;
 @class GPBFieldOptions;
+@class GPBFileDescriptorProto;
 @class GPBFileOptions;
+@class GPBGeneratedCodeInfo_Annotation;
 @class GPBMessageOptions;
+@class GPBMethodDescriptorProto;
 @class GPBMethodOptions;
+@class GPBOneofDescriptorProto;
+@class GPBServiceDescriptorProto;
 @class GPBServiceOptions;
 @class GPBSourceCodeInfo;
+@class GPBSourceCodeInfo_Location;
+@class GPBUninterpretedOption;
+@class GPBUninterpretedOption_NamePart;
 
 NS_ASSUME_NONNULL_BEGIN
 
@@ -151,8 +165,7 @@
 // files it parses.
 @interface GPBFileDescriptorSet : GPBMessage
 
-// |fileArray| contains |GPBFileDescriptorProto|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *fileArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBFileDescriptorProto*> *fileArray;
 @property(nonatomic, readonly) NSUInteger fileArray_Count;
 
 @end
@@ -186,8 +199,7 @@
 @property(nonatomic, readwrite, copy, null_resettable) NSString *package;
 
 // Names of files imported by this file.
-// |dependencyArray| contains |NSString|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *dependencyArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<NSString*> *dependencyArray;
 @property(nonatomic, readonly) NSUInteger dependencyArray_Count;
 
 // Indexes of the public imported files in the dependency list above.
@@ -200,20 +212,16 @@
 @property(nonatomic, readonly) NSUInteger weakDependencyArray_Count;
 
 // All top-level definitions in this file.
-// |messageTypeArray| contains |GPBDescriptorProto|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *messageTypeArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBDescriptorProto*> *messageTypeArray;
 @property(nonatomic, readonly) NSUInteger messageTypeArray_Count;
 
-// |enumTypeArray| contains |GPBEnumDescriptorProto|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *enumTypeArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBEnumDescriptorProto*> *enumTypeArray;
 @property(nonatomic, readonly) NSUInteger enumTypeArray_Count;
 
-// |serviceArray| contains |GPBServiceDescriptorProto|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *serviceArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBServiceDescriptorProto*> *serviceArray;
 @property(nonatomic, readonly) NSUInteger serviceArray_Count;
 
-// |extensionArray| contains |GPBFieldDescriptorProto|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *extensionArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBFieldDescriptorProto*> *extensionArray;
 @property(nonatomic, readonly) NSUInteger extensionArray_Count;
 
 @property(nonatomic, readwrite) BOOL hasOptions;
@@ -254,41 +262,33 @@
 @property(nonatomic, readwrite) BOOL hasName;
 @property(nonatomic, readwrite, copy, null_resettable) NSString *name;
 
-// |fieldArray| contains |GPBFieldDescriptorProto|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *fieldArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBFieldDescriptorProto*> *fieldArray;
 @property(nonatomic, readonly) NSUInteger fieldArray_Count;
 
-// |extensionArray| contains |GPBFieldDescriptorProto|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *extensionArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBFieldDescriptorProto*> *extensionArray;
 @property(nonatomic, readonly) NSUInteger extensionArray_Count;
 
-// |nestedTypeArray| contains |GPBDescriptorProto|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *nestedTypeArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBDescriptorProto*> *nestedTypeArray;
 @property(nonatomic, readonly) NSUInteger nestedTypeArray_Count;
 
-// |enumTypeArray| contains |GPBEnumDescriptorProto|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *enumTypeArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBEnumDescriptorProto*> *enumTypeArray;
 @property(nonatomic, readonly) NSUInteger enumTypeArray_Count;
 
-// |extensionRangeArray| contains |GPBDescriptorProto_ExtensionRange|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *extensionRangeArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBDescriptorProto_ExtensionRange*> *extensionRangeArray;
 @property(nonatomic, readonly) NSUInteger extensionRangeArray_Count;
 
-// |oneofDeclArray| contains |GPBOneofDescriptorProto|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *oneofDeclArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBOneofDescriptorProto*> *oneofDeclArray;
 @property(nonatomic, readonly) NSUInteger oneofDeclArray_Count;
 
 @property(nonatomic, readwrite) BOOL hasOptions;
 @property(nonatomic, readwrite, strong, null_resettable) GPBMessageOptions *options;
 
-// |reservedRangeArray| contains |GPBDescriptorProto_ReservedRange|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *reservedRangeArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBDescriptorProto_ReservedRange*> *reservedRangeArray;
 @property(nonatomic, readonly) NSUInteger reservedRangeArray_Count;
 
 // Reserved field names, which may not be used by fields in the same message.
 // A given name may only be reserved once.
-// |reservedNameArray| contains |NSString|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *reservedNameArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<NSString*> *reservedNameArray;
 @property(nonatomic, readonly) NSUInteger reservedNameArray_Count;
 
 @end
@@ -430,8 +430,7 @@
 @property(nonatomic, readwrite) BOOL hasName;
 @property(nonatomic, readwrite, copy, null_resettable) NSString *name;
 
-// |valueArray| contains |GPBEnumValueDescriptorProto|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *valueArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBEnumValueDescriptorProto*> *valueArray;
 @property(nonatomic, readonly) NSUInteger valueArray_Count;
 
 @property(nonatomic, readwrite) BOOL hasOptions;
@@ -475,8 +474,7 @@
 @property(nonatomic, readwrite) BOOL hasName;
 @property(nonatomic, readwrite, copy, null_resettable) NSString *name;
 
-// |methodArray| contains |GPBMethodDescriptorProto|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *methodArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBMethodDescriptorProto*> *methodArray;
 @property(nonatomic, readonly) NSUInteger methodArray_Count;
 
 @property(nonatomic, readwrite) BOOL hasOptions;
@@ -650,8 +648,7 @@
 @property(nonatomic, readwrite) BOOL javananoUseDeprecatedPackage;
 
 // The parser stores options it doesn't recognize here. See above.
-// |uninterpretedOptionArray| contains |GPBUninterpretedOption|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *uninterpretedOptionArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBUninterpretedOption*> *uninterpretedOptionArray;
 @property(nonatomic, readonly) NSUInteger uninterpretedOptionArray_Count;
 
 @end
@@ -727,8 +724,7 @@
 @property(nonatomic, readwrite) BOOL mapEntry;
 
 // The parser stores options it doesn't recognize here. See above.
-// |uninterpretedOptionArray| contains |GPBUninterpretedOption|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *uninterpretedOptionArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBUninterpretedOption*> *uninterpretedOptionArray;
 @property(nonatomic, readonly) NSUInteger uninterpretedOptionArray_Count;
 
 @end
@@ -817,8 +813,7 @@
 @property(nonatomic, readwrite) BOOL weak;
 
 // The parser stores options it doesn't recognize here. See above.
-// |uninterpretedOptionArray| contains |GPBUninterpretedOption|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *uninterpretedOptionArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBUninterpretedOption*> *uninterpretedOptionArray;
 @property(nonatomic, readonly) NSUInteger uninterpretedOptionArray_Count;
 
 @end
@@ -846,8 +841,7 @@
 @property(nonatomic, readwrite) BOOL deprecated;
 
 // The parser stores options it doesn't recognize here. See above.
-// |uninterpretedOptionArray| contains |GPBUninterpretedOption|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *uninterpretedOptionArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBUninterpretedOption*> *uninterpretedOptionArray;
 @property(nonatomic, readonly) NSUInteger uninterpretedOptionArray_Count;
 
 @end
@@ -869,8 +863,7 @@
 @property(nonatomic, readwrite) BOOL deprecated;
 
 // The parser stores options it doesn't recognize here. See above.
-// |uninterpretedOptionArray| contains |GPBUninterpretedOption|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *uninterpretedOptionArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBUninterpretedOption*> *uninterpretedOptionArray;
 @property(nonatomic, readonly) NSUInteger uninterpretedOptionArray_Count;
 
 @end
@@ -892,8 +885,7 @@
 @property(nonatomic, readwrite) BOOL deprecated;
 
 // The parser stores options it doesn't recognize here. See above.
-// |uninterpretedOptionArray| contains |GPBUninterpretedOption|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *uninterpretedOptionArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBUninterpretedOption*> *uninterpretedOptionArray;
 @property(nonatomic, readonly) NSUInteger uninterpretedOptionArray_Count;
 
 @end
@@ -915,8 +907,7 @@
 @property(nonatomic, readwrite) BOOL deprecated;
 
 // The parser stores options it doesn't recognize here. See above.
-// |uninterpretedOptionArray| contains |GPBUninterpretedOption|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *uninterpretedOptionArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBUninterpretedOption*> *uninterpretedOptionArray;
 @property(nonatomic, readonly) NSUInteger uninterpretedOptionArray_Count;
 
 @end
@@ -941,8 +932,7 @@
 // in them.
 @interface GPBUninterpretedOption : GPBMessage
 
-// |nameArray| contains |GPBUninterpretedOption_NamePart|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *nameArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBUninterpretedOption_NamePart*> *nameArray;
 @property(nonatomic, readonly) NSUInteger nameArray_Count;
 
 // The value of the uninterpreted option, in whatever type the tokenizer
@@ -1042,8 +1032,7 @@
 // - Code which tries to interpret locations should probably be designed to
 //   ignore those that it doesn't understand, as more types of locations could
 //   be recorded in the future.
-// |locationArray| contains |GPBSourceCodeInfo_Location|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *locationArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBSourceCodeInfo_Location*> *locationArray;
 @property(nonatomic, readonly) NSUInteger locationArray_Count;
 
 @end
@@ -1147,8 +1136,7 @@
 @property(nonatomic, readwrite) BOOL hasTrailingComments;
 @property(nonatomic, readwrite, copy, null_resettable) NSString *trailingComments;
 
-// |leadingDetachedCommentsArray| contains |NSString|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *leadingDetachedCommentsArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<NSString*> *leadingDetachedCommentsArray;
 @property(nonatomic, readonly) NSUInteger leadingDetachedCommentsArray_Count;
 
 @end
@@ -1166,8 +1154,7 @@
 
 // An Annotation connects some span of text in generated code to an element
 // of its generating .proto file.
-// |annotationArray| contains |GPBGeneratedCodeInfo_Annotation|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *annotationArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBGeneratedCodeInfo_Annotation*> *annotationArray;
 @property(nonatomic, readonly) NSUInteger annotationArray_Count;
 
 @end
diff --git a/objectivec/google/protobuf/FieldMask.pbobjc.h b/objectivec/google/protobuf/FieldMask.pbobjc.h
index 4e4ec38..f4bc265 100644
--- a/objectivec/google/protobuf/FieldMask.pbobjc.h
+++ b/objectivec/google/protobuf/FieldMask.pbobjc.h
@@ -155,8 +155,7 @@
 @interface GPBFieldMask : GPBMessage
 
 // The set of field mask paths.
-// |pathsArray| contains |NSString|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *pathsArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<NSString*> *pathsArray;
 @property(nonatomic, readonly) NSUInteger pathsArray_Count;
 
 @end
diff --git a/objectivec/google/protobuf/Struct.pbobjc.h b/objectivec/google/protobuf/Struct.pbobjc.h
index f40414f..293dea4 100644
--- a/objectivec/google/protobuf/Struct.pbobjc.h
+++ b/objectivec/google/protobuf/Struct.pbobjc.h
@@ -13,6 +13,7 @@
 
 @class GPBListValue;
 @class GPBStruct;
+@class GPBValue;
 
 NS_ASSUME_NONNULL_BEGIN
 
@@ -60,8 +61,7 @@
 @interface GPBStruct : GPBMessage
 
 // Map of dynamically typed values.
-// |fields| values are |GPBValue|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableDictionary *fields;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableDictionary<NSString*, GPBValue*> *fields;
 @property(nonatomic, readonly) NSUInteger fields_Count;
 
 @end
@@ -135,8 +135,7 @@
 @interface GPBListValue : GPBMessage
 
 // Repeated field of dynamically typed values.
-// |valuesArray| contains |GPBValue|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *valuesArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBValue*> *valuesArray;
 @property(nonatomic, readonly) NSUInteger valuesArray_Count;
 
 @end
diff --git a/objectivec/google/protobuf/Type.pbobjc.h b/objectivec/google/protobuf/Type.pbobjc.h
index e4c7a25..a7d03a2 100644
--- a/objectivec/google/protobuf/Type.pbobjc.h
+++ b/objectivec/google/protobuf/Type.pbobjc.h
@@ -12,6 +12,9 @@
 CF_EXTERN_C_BEGIN
 
 @class GPBAny;
+@class GPBEnumValue;
+@class GPBField;
+@class GPBOption;
 @class GPBSourceContext;
 
 NS_ASSUME_NONNULL_BEGIN
@@ -150,18 +153,15 @@
 @property(nonatomic, readwrite, copy, null_resettable) NSString *name;
 
 // The list of fields.
-// |fieldsArray| contains |GPBField|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *fieldsArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBField*> *fieldsArray;
 @property(nonatomic, readonly) NSUInteger fieldsArray_Count;
 
 // The list of types appearing in `oneof` definitions in this type.
-// |oneofsArray| contains |NSString|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *oneofsArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<NSString*> *oneofsArray;
 @property(nonatomic, readonly) NSUInteger oneofsArray_Count;
 
 // The protocol buffer options.
-// |optionsArray| contains |GPBOption|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *optionsArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBOption*> *optionsArray;
 @property(nonatomic, readonly) NSUInteger optionsArray_Count;
 
 // The source context.
@@ -218,8 +218,7 @@
 @property(nonatomic, readwrite) BOOL packed;
 
 // The protocol buffer options.
-// |optionsArray| contains |GPBOption|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *optionsArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBOption*> *optionsArray;
 @property(nonatomic, readonly) NSUInteger optionsArray_Count;
 
 // The field JSON name.
@@ -253,13 +252,11 @@
 @property(nonatomic, readwrite, copy, null_resettable) NSString *name;
 
 // Enum value definitions.
-// |enumvalueArray| contains |GPBEnumValue|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *enumvalueArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBEnumValue*> *enumvalueArray;
 @property(nonatomic, readonly) NSUInteger enumvalueArray_Count;
 
 // Protocol buffer options.
-// |optionsArray| contains |GPBOption|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *optionsArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBOption*> *optionsArray;
 @property(nonatomic, readonly) NSUInteger optionsArray_Count;
 
 // The source context.
@@ -292,8 +289,7 @@
 @property(nonatomic, readwrite) int32_t number;
 
 // Protocol buffer options.
-// |optionsArray| contains |GPBOption|
-@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *optionsArray;
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBOption*> *optionsArray;
 @property(nonatomic, readonly) NSUInteger optionsArray_Count;
 
 @end
diff --git a/protobuf.bzl b/protobuf.bzl
index f674a6c..399cf62 100644
--- a/protobuf.bzl
+++ b/protobuf.bzl
@@ -63,6 +63,10 @@
   if ctx.attr.gen_py:
     args += ["--python_out=" + ctx.var["GENDIR"] + "/" + gen_dir]
 
+  if ctx.executable.grpc_cpp_plugin:
+    args += ["--plugin=protoc-gen-grpc=" + ctx.executable.grpc_cpp_plugin.path]
+    args += ["--grpc_out=" + ctx.var["GENDIR"] + "/" + gen_dir]
+
   if args:
     ctx.action(
         inputs=srcs + deps,
@@ -90,6 +94,11 @@
             single_file = True,
             mandatory = True,
         ),
+        "grpc_cpp_plugin": attr.label(
+            cfg = HOST_CFG,
+            executable = True,
+            single_file = True,
+        ),
         "gen_cc": attr.bool(),
         "gen_py": attr.bool(),
         "outs": attr.output_list(),
@@ -106,6 +115,7 @@
         include=None,
         protoc="//google/protobuf:protoc",
         internal_bootstrap_hack=False,
+        use_grpc_plugin=False,
         default_runtime="//google/protobuf:protobuf",
         **kargs):
   """Bazel rule to create a C++ protobuf library from proto source files
@@ -126,6 +136,8 @@
         for bootstraping. When it is set to True, no files will be generated.
         The rule will simply be a provider for .proto files, so that other
         cc_proto_library can depend on it.
+    use_grpc_plugin: a flag to indicate whether to call the grpc C++ plugin
+        when processing the proto files.
     default_runtime: the implicitly default runtime which will be depended on by
         the generated cc_library target.
     **kargs: other keyword arguments that are passed to cc_library.
@@ -153,6 +165,10 @@
         **kargs)
     return
 
+  grpc_cpp_plugin = None
+  if use_grpc_plugin:
+    grpc_cpp_plugin = "//external:grpc_cpp_plugin"
+
   outs = _CcOuts(srcs)
   _proto_gen(
       name=name + "_genproto",
@@ -160,6 +176,7 @@
       deps=[s + "_genproto" for s in deps],
       includes=includes,
       protoc=protoc,
+      grpc_cpp_plugin=grpc_cpp_plugin,
       gen_cc=1,
       outs=outs,
       visibility=["//visibility:public"],
diff --git a/src/google/protobuf/compiler/js/js_generator.cc b/src/google/protobuf/compiler/js/js_generator.cc
index e6c3b36..351c396 100755
--- a/src/google/protobuf/compiler/js/js_generator.cc
+++ b/src/google/protobuf/compiler/js/js_generator.cc
@@ -134,12 +134,37 @@
 
 // Returns a copy of |filename| with any trailing ".protodevel" or ".proto
 // suffix stripped.
+// TODO(robinson): Unify with copy in compiler/cpp/internal/helpers.cc.
 string StripProto(const string& filename) {
   const char* suffix = HasSuffixString(filename, ".protodevel")
       ? ".protodevel" : ".proto";
   return StripSuffixString(filename, suffix);
 }
 
+// Given a filename like foo/bar/baz.proto, returns the correspoding JavaScript
+// file foo/bar/baz.js.
+string GetJSFilename(const string& filename) {
+  const char* suffix = HasSuffixString(filename, ".protodevel")
+      ? ".protodevel" : ".proto";
+  return StripSuffixString(filename, suffix) + "_pb.js";
+}
+
+// Returns the alias we assign to the module of the given .proto filename
+// when importing.
+string ModuleAlias(const string& filename) {
+  // This scheme could technically cause problems if a file includes any 2 of:
+  //   foo/bar_baz.proto
+  //   foo_bar_baz.proto
+  //   foo_bar/baz.proto
+  //
+  // We'll worry about this problem if/when we actually see it.  This name isn't
+  // exposed to users so we can change it later if we need to.
+  string basename = StripProto(filename);
+  StripString(&basename, "-", '$');
+  StripString(&basename, "/", '_');
+  return basename + "_pb";
+}
+
 // Returns the fully normalized JavaScript path for the given
 // file descriptor's package.
 string GetPath(const GeneratorOptions& options,
@@ -215,6 +240,26 @@
       value_descriptor->type()) + "." + value_descriptor->name();
 }
 
+string MaybeCrossFileRef(const GeneratorOptions& options,
+                         const FileDescriptor* from_file,
+                         const Descriptor* to_message) {
+  if (options.import_style == GeneratorOptions::IMPORT_COMMONJS &&
+      from_file != to_message->file()) {
+    // Cross-file ref in CommonJS needs to use the module alias instead of
+    // the global name.
+    return ModuleAlias(to_message->file()->name()) + "." + to_message->name();
+  } else {
+    // Within a single file we use a full name.
+    return GetPath(options, to_message);
+  }
+}
+
+string SubmessageTypeRef(const GeneratorOptions& options,
+                         const FieldDescriptor* field) {
+  GOOGLE_CHECK(field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE);
+  return MaybeCrossFileRef(options, field->file(), field->message_type());
+}
+
 // - Object field name: LOWER_UNDERSCORE -> LOWER_CAMEL, except for group fields
 // (UPPER_CAMEL -> LOWER_CAMEL), with "List" (or "Map") appended if appropriate,
 // and with reserved words triggering a "pb_" prefix.
@@ -952,11 +997,13 @@
 }
 
 string JSExtensionsObjectName(const GeneratorOptions& options,
+                              const FileDescriptor* from_file,
                               const Descriptor* desc) {
   if (desc->full_name() == "google.protobuf.bridge.MessageSet") {
+    // TODO(haberman): fix this for the IMPORT_COMMONJS case.
     return "jspb.Message.messageSetExtensions";
   } else {
-    return GetPath(options, desc) + ".extensions";
+    return MaybeCrossFileRef(options, from_file, desc) + ".extensions";
   }
 }
 
@@ -1113,19 +1160,24 @@
                  "\n");
 }
 
+void Generator::FindProvidesForFile(const GeneratorOptions& options,
+                                    io::Printer* printer,
+                                    const FileDescriptor* file,
+                                    std::set<string>* provided) const {
+  for (int i = 0; i < file->message_type_count(); i++) {
+    FindProvidesForMessage(options, printer, file->message_type(i), provided);
+  }
+  for (int i = 0; i < file->enum_type_count(); i++) {
+    FindProvidesForEnum(options, printer, file->enum_type(i), provided);
+  }
+}
+
 void Generator::FindProvides(const GeneratorOptions& options,
                              io::Printer* printer,
                              const vector<const FileDescriptor*>& files,
                              std::set<string>* provided) const {
   for (int i = 0; i < files.size(); i++) {
-    for (int j = 0; j < files[i]->message_type_count(); j++) {
-      FindProvidesForMessage(options, printer, files[i]->message_type(j),
-                                 provided);
-    }
-    for (int j = 0; j < files[i]->enum_type_count(); j++) {
-      FindProvidesForEnum(options, printer, files[i]->enum_type(j),
-                              provided);
-    }
+    FindProvidesForFile(options, printer, files[i], provided);
   }
 
   printer->Print("\n");
@@ -1204,38 +1256,45 @@
                                  io::Printer* printer,
                                  const vector<const FileDescriptor*>& files,
                                  std::set<string>* provided) const {
-  std::set<string> required;
-  std::set<string> forwards;
-  bool have_extensions = false;
-  bool have_message = false;
+  if (options.import_style == GeneratorOptions::IMPORT_BROWSER) {
+    return;
+  } else if (options.import_style == GeneratorOptions::IMPORT_CLOSURE) {
+    // For Closure imports we need to import every message type individually.
+    std::set<string> required;
+    std::set<string> forwards;
+    bool have_extensions = false;
+    bool have_message = false;
 
-  for (int i = 0; i < files.size(); i++) {
-    for (int j = 0; j < files[i]->message_type_count(); j++) {
-      FindRequiresForMessage(options,
-                             files[i]->message_type(j),
-                             &required, &forwards, &have_message);
-    }
-    if (!have_extensions && HasExtensions(files[i])) {
-      have_extensions = true;
+    for (int i = 0; i < files.size(); i++) {
+      for (int j = 0; j < files[i]->message_type_count(); j++) {
+        FindRequiresForMessage(options,
+                               files[i]->message_type(j),
+                               &required, &forwards, &have_message);
+      }
+      if (!have_extensions && HasExtensions(files[i])) {
+        have_extensions = true;
+      }
+
+      for (int j = 0; j < files[i]->extension_count(); j++) {
+        const FieldDescriptor* extension = files[i]->extension(j);
+        if (IgnoreField(extension)) {
+          continue;
+        }
+        if (extension->containing_type()->full_name() !=
+            "google.protobuf.bridge.MessageSet") {
+          required.insert(GetPath(options, extension->containing_type()));
+        }
+        FindRequiresForField(options, extension, &required, &forwards);
+        have_extensions = true;
+      }
     }
 
-    for (int j = 0; j < files[i]->extension_count(); j++) {
-      const FieldDescriptor* extension = files[i]->extension(j);
-      if (IgnoreField(extension)) {
-        continue;
-      }
-      if (extension->containing_type()->full_name() !=
-          "google.protobuf.bridge.MessageSet") {
-        required.insert(GetPath(options, extension->containing_type()));
-      }
-      FindRequiresForField(options, extension, &required, &forwards);
-      have_extensions = true;
-    }
+    GenerateRequiresImpl(options, printer, &required, &forwards, provided,
+                         /* require_jspb = */ have_message,
+                         /* require_extension = */ have_extensions);
+  } else if (options.import_style == GeneratorOptions::IMPORT_COMMONJS) {
+    // CommonJS imports are based on files
   }
-
-  GenerateRequiresImpl(options, printer, &required, &forwards, provided,
-                       /* require_jspb = */ have_message,
-                       /* require_extension = */ have_extensions);
 }
 
 void Generator::GenerateRequires(const GeneratorOptions& options,
@@ -1406,6 +1465,12 @@
     if (IsExtendable(desc) && desc->full_name() != "google.protobuf.bridge.MessageSet") {
       GenerateClassExtensionFieldInfo(options, printer, desc);
     }
+
+    if (options.import_style != GeneratorOptions:: IMPORT_CLOSURE) {
+      for (int i = 0; i < desc->extension_count(); i++) {
+        GenerateExtension(options, printer, desc->extension(i));
+      }
+    }
   }
 
   // Recurse on nested types.
@@ -1623,7 +1688,7 @@
         "obj,\n"
         "      $extObject$, $class$.prototype.getExtension,\n"
         "      includeInstance);\n",
-        "extObject", JSExtensionsObjectName(options, desc),
+        "extObject", JSExtensionsObjectName(options, desc->file(), desc),
         "class", GetPath(options, desc));
   }
 
@@ -1652,13 +1717,13 @@
         printer->Print("jspb.Message.toObjectList(msg.get$getter$(),\n"
                        "    $type$.toObject, includeInstance)",
                        "getter", JSGetterName(field),
-                       "type", GetPath(options, field->message_type()));
+                       "type", SubmessageTypeRef(options, field));
       }
     } else {
       printer->Print("(f = msg.get$getter$()) && "
                      "$type$.toObject(includeInstance, f)",
                      "getter", JSGetterName(field),
-                     "type", GetPath(options, field->message_type()));
+                     "type", SubmessageTypeRef(options, field));
     }
   } else {
     // Simple field (singular or repeated).
@@ -1723,7 +1788,7 @@
             "      }));\n",
             "name", JSObjectFieldName(field),
             "index", JSFieldIndex(field),
-            "fieldclass", GetPath(options, field->message_type()));
+            "fieldclass", SubmessageTypeRef(options, field));
       }
     } else {
       printer->Print(
@@ -1731,7 +1796,7 @@
           "      msg, $index$, $fieldclass$.fromObject(obj.$name$));\n",
           "name", JSObjectFieldName(field),
           "index", JSFieldIndex(field),
-          "fieldclass", GetPath(options, field->message_type()));
+          "fieldclass", SubmessageTypeRef(options, field));
     }
   } else {
     // Simple (primitive) field.
@@ -1815,7 +1880,7 @@
                                       /* always_singular = */ false),
         "rpt", (field->is_repeated() ? "Repeated" : ""),
         "index", JSFieldIndex(field),
-        "wrapperclass", GetPath(options, field->message_type()),
+        "wrapperclass", SubmessageTypeRef(options, field),
         "required", (field->label() == FieldDescriptor::LABEL_REQUIRED ?
                      ", 1" : ""));
     printer->Print(
@@ -2043,7 +2108,7 @@
         "        $class$.prototype.getExtension,\n"
         "        $class$.prototype.setExtension);\n"
         "      break;\n",
-        "extobj", JSExtensionsObjectName(options, desc),
+        "extobj", JSExtensionsObjectName(options, desc->file(), desc),
         "class", GetPath(options, desc));
   } else {
     printer->Print(
@@ -2073,7 +2138,7 @@
         "      var value = new $fieldclass$;\n"
         "      reader.read$msgOrGroup$($grpfield$value,"
         "$fieldclass$.deserializeBinaryFromReader);\n",
-        "fieldclass", GetPath(options, field->message_type()),
+        "fieldclass", SubmessageTypeRef(options, field),
         "msgOrGroup", (field->type() == FieldDescriptor::TYPE_GROUP) ?
                       "Group" : "Message",
         "grpfield", (field->type() == FieldDescriptor::TYPE_GROUP) ?
@@ -2149,7 +2214,7 @@
     printer->Print(
         "  jspb.Message.serializeBinaryExtensions(this, writer, $extobj$,\n"
         "    $class$.prototype.getExtension);\n",
-        "extobj", JSExtensionsObjectName(options, desc),
+        "extobj", JSExtensionsObjectName(options, desc->file(), desc),
         "class", GetPath(options, desc));
   }
 
@@ -2222,7 +2287,7 @@
     printer->Print(
         ",\n"
         "      $submsg$.serializeBinaryToWriter\n",
-        "submsg", GetPath(options, field->message_type()));
+        "submsg", SubmessageTypeRef(options, field));
   } else {
     printer->Print("\n");
   }
@@ -2290,9 +2355,9 @@
       "index", SimpleItoa(field->number()),
       "name", JSObjectFieldName(field),
       "ctor", (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ?
-               GetPath(options, field->message_type()) : string("null")),
+               SubmessageTypeRef(options, field) : string("null")),
       "toObject", (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ?
-                   (GetPath(options, field->message_type()) + ".toObject") :
+                   (SubmessageTypeRef(options, field) + ".toObject") :
                    string("null")),
       "repeated", (field->is_repeated() ? "1" : "0"));
 
@@ -2308,11 +2373,11 @@
         "binaryWriterFn", JSBinaryWriterMethodName(field),
         "binaryMessageSerializeFn",
         (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) ?
-        (GetPath(options, field->message_type()) +
+        (SubmessageTypeRef(options, field) +
          ".serializeBinaryToWriter") : "null",
         "binaryMessageDeserializeFn",
         (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) ?
-        (GetPath(options, field->message_type()) +
+        (SubmessageTypeRef(options, field) +
          ".deserializeBinaryFromReader") : "null",
         "isPacked", (field->is_packed() ? "true" : "false"));
   } else {
@@ -2324,7 +2389,8 @@
       "// toObject() will function correctly.\n"
       "$extendName$[$index$] = $class$.$name$;\n"
       "\n",
-      "extendName", JSExtensionsObjectName(options, field->containing_type()),
+      "extendName", JSExtensionsObjectName(options, field->file(),
+                                           field->containing_type()),
       "index", SimpleItoa(field->number()),
       "class", extension_scope,
       "name", JSObjectFieldName(field));
@@ -2364,6 +2430,19 @@
       namespace_prefix = options[i].second;
     } else if (options[i].first == "library") {
       library = options[i].second;
+    } else if (options[i].first == "import_style") {
+      if (options[i].second == "closure") {
+        import_style = IMPORT_CLOSURE;
+      } else if (options[i].second == "commonjs") {
+        import_style = IMPORT_COMMONJS;
+      } else if (options[i].second == "browser") {
+        import_style = IMPORT_BROWSER;
+      } else if (options[i].second == "es6") {
+        import_style = IMPORT_ES6;
+      } else {
+        *error = "Unknown import style " + options[i].second + ", expected " +
+                 "one of: closure, commonjs, browser, es6.";
+      }
     } else {
       // Assume any other option is an output directory, as long as it is a bare
       // `key` rather than a `key=value` option.
@@ -2375,6 +2454,11 @@
     }
   }
 
+  if (!library.empty() && import_style != IMPORT_CLOSURE) {
+    *error = "The library option should only be used for "
+             "import_style=closure";
+  }
+
   return true;
 }
 
@@ -2418,6 +2502,63 @@
   }
 }
 
+void Generator::GenerateFile(const GeneratorOptions& options,
+                             io::Printer* printer,
+                             const FileDescriptor* file) const {
+  GenerateHeader(options, printer);
+
+  // Generate "require" statements.
+  if (options.import_style == GeneratorOptions::IMPORT_COMMONJS) {
+    printer->Print("var jspb = require('google-protobuf');\n");
+    printer->Print("var goog = jspb;\n");
+    printer->Print("var global = Function('return this')();\n\n");
+
+    for (int i = 0; i < file->dependency_count(); i++) {
+      const std::string& name = file->dependency(i)->name();
+      printer->Print(
+          "var $alias$ = require('$file$');\n",
+          "alias", ModuleAlias(name),
+          "file", GetJSFilename(name));
+    }
+  }
+
+  // We aren't using Closure's import system, but we use goog.exportSymbol()
+  // to construct the expected tree of objects, eg.
+  //
+  //   goog.exportSymbol('foo.bar.Baz', null, this);
+  //
+  //   // Later generated code expects foo.bar = {} to exist:
+  //   foo.bar.Baz = function() { /* ... */ }
+  std::set<std::string> provided;
+
+  // Cover the case where this file declares extensions but no messages.
+  // This will ensure that the file-level object will be declared to hold
+  // the extensions.
+  for (int i = 0; i < file->extension_count(); i++) {
+    provided.insert(file->extension(i)->full_name());
+  }
+
+  FindProvidesForFile(options, printer, file, &provided);
+  for (std::set<string>::iterator it = provided.begin();
+       it != provided.end(); ++it) {
+    printer->Print("goog.exportSymbol('$name$', null, global);\n",
+                   "name", *it);
+  }
+
+  GenerateClassesAndEnums(options, printer, file);
+
+  // Extensions nested inside messages are emitted inside
+  // GenerateClassesAndEnums().
+  for (int i = 0; i < file->extension_count(); i++) {
+    GenerateExtension(options, printer, file->extension(i));
+  }
+
+  if (options.import_style == GeneratorOptions::IMPORT_COMMONJS) {
+    printer->Print("goog.object.extend(exports, $package$);\n",
+                   "package", GetPath(options, file));
+  }
+}
+
 bool Generator::GenerateAll(const vector<const FileDescriptor*>& files,
                             const string& parameter,
                             GeneratorContext* context,
@@ -2430,10 +2571,14 @@
   }
 
 
-  // We're either generating a single library file with definitions for message
-  // and enum types in *all* FileDescriptor inputs, or we're generating a single
-  // file for each type.
-  if (options.library != "") {
+  // There are three schemes for where output files go:
+  //
+  // - import_style = IMPORT_CLOSURE, library non-empty: all output in one file
+  // - import_style = IMPORT_CLOSURE, library empty: one output file per type
+  // - import_style != IMPORT_CLOSURE: one output file per .proto file
+  if (options.import_style == GeneratorOptions::IMPORT_CLOSURE &&
+      options.library != "") {
+    // All output should go in a single file.
     string filename = options.output_dir + "/" + options.library + ".js";
     google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(context->Open(filename));
     GOOGLE_CHECK(output.get());
@@ -2469,7 +2614,7 @@
     if (printer.failed()) {
       return false;
     }
-  } else {
+  } else if (options.import_style == GeneratorOptions::IMPORT_CLOSURE) {
     // Collect all types, and print each type to a separate file. Pull out
     // free-floating extensions while we make this pass.
     map< string, vector<const FieldDescriptor*> > extensions_by_namespace;
@@ -2611,6 +2756,24 @@
         }
       }
     }
+  } else {
+    // Generate one output file per input (.proto) file.
+
+    for (int i = 0; i < files.size(); i++) {
+      const google::protobuf::FileDescriptor* file = files[i];
+
+      string filename = options.output_dir + "/" + GetJSFilename(file->name());
+      google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(
+          context->Open(filename));
+      GOOGLE_CHECK(output.get());
+      io::Printer printer(output.get(), '$');
+
+      GenerateFile(options, &printer, file);
+
+      if (printer.failed()) {
+        return false;
+      }
+    }
   }
 
   return true;
diff --git a/src/google/protobuf/compiler/js/js_generator.h b/src/google/protobuf/compiler/js/js_generator.h
index db2dceb..db9178d 100755
--- a/src/google/protobuf/compiler/js/js_generator.h
+++ b/src/google/protobuf/compiler/js/js_generator.h
@@ -67,6 +67,13 @@
   bool error_on_name_conflict;
   // Enable binary-format support?
   bool binary;
+  // What style of imports should be used.
+  enum ImportStyle {
+    IMPORT_CLOSURE,    // goog.require()
+    IMPORT_COMMONJS,   // require()
+    IMPORT_BROWSER,    // no import statements
+    IMPORT_ES6,        // import { member } from ''
+  } import_style;
 
   GeneratorOptions()
       : add_require_for_enums(false),
@@ -75,7 +82,8 @@
         namespace_prefix(""),
         library(""),
         error_on_name_conflict(false),
-        binary(false) {}
+        binary(false),
+        import_style(IMPORT_CLOSURE) {}
 
   bool ParseFromOptions(
       const vector< pair< string, string > >& options,
@@ -111,6 +119,10 @@
                     io::Printer* printer,
                     const vector<const FileDescriptor*>& file,
                     std::set<string>* provided) const;
+  void FindProvidesForFile(const GeneratorOptions& options,
+                           io::Printer* printer,
+                           const FileDescriptor* file,
+                           std::set<string>* provided) const;
   void FindProvidesForMessage(const GeneratorOptions& options,
                               io::Printer* printer,
                               const Descriptor* desc,
@@ -168,6 +180,10 @@
                                 std::set<string>* required,
                                 std::set<string>* forwards) const;
 
+  void GenerateFile(const GeneratorOptions& options,
+                    io::Printer* printer,
+                    const FileDescriptor* file) const;
+
   // Generate definitions for all message classes and enums in all files,
   // processing the files in dependence order.
   void GenerateFilesInDepOrder(const GeneratorOptions& options,
diff --git a/src/google/protobuf/compiler/main.cc b/src/google/protobuf/compiler/main.cc
index 97df536..66ad13b 100644
--- a/src/google/protobuf/compiler/main.cc
+++ b/src/google/protobuf/compiler/main.cc
@@ -78,7 +78,7 @@
 
   // Objective C
   google::protobuf::compiler::objectivec::ObjectiveCGenerator objc_generator;
-  cli.RegisterGenerator("--objc_out", &objc_generator,
+  cli.RegisterGenerator("--objc_out", "--objc_opt", &objc_generator,
                         "Generate Objective C header and source.");
 
   // JavaScript
diff --git a/src/google/protobuf/compiler/objectivec/objectivec_enum_field.cc b/src/google/protobuf/compiler/objectivec/objectivec_enum_field.cc
index 30a13dd..ecc77f6 100644
--- a/src/google/protobuf/compiler/objectivec/objectivec_enum_field.cc
+++ b/src/google/protobuf/compiler/objectivec/objectivec_enum_field.cc
@@ -44,6 +44,7 @@
 namespace objectivec {
 
 namespace {
+
 void SetEnumVariables(const FieldDescriptor* descriptor,
                       map<string, string>* variables) {
   string type = EnumName(descriptor->enum_type());
@@ -63,8 +64,9 @@
 }
 }  // namespace
 
-EnumFieldGenerator::EnumFieldGenerator(const FieldDescriptor* descriptor)
-    : SingleFieldGenerator(descriptor) {
+EnumFieldGenerator::EnumFieldGenerator(const FieldDescriptor* descriptor,
+                                       const Options& options)
+    : SingleFieldGenerator(descriptor, options) {
   SetEnumVariables(descriptor, &variables_);
 }
 
@@ -112,6 +114,7 @@
 
 void EnumFieldGenerator::DetermineForwardDeclarations(
     set<string>* fwd_decls) const {
+  SingleFieldGenerator::DetermineForwardDeclarations(fwd_decls);
   // If it is an enum defined in a different file, then we'll need a forward
   // declaration for it.  When it is in our file, all the enums are output
   // before the message, so it will be declared before it is needed.
@@ -123,14 +126,20 @@
 }
 
 RepeatedEnumFieldGenerator::RepeatedEnumFieldGenerator(
-    const FieldDescriptor* descriptor)
-    : RepeatedFieldGenerator(descriptor) {
+    const FieldDescriptor* descriptor, const Options& options)
+    : RepeatedFieldGenerator(descriptor, options) {
   SetEnumVariables(descriptor, &variables_);
   variables_["array_storage_type"] = "GPBEnumArray";
 }
 
 RepeatedEnumFieldGenerator::~RepeatedEnumFieldGenerator() {}
 
+void RepeatedEnumFieldGenerator::FinishInitialization(void) {
+  RepeatedFieldGenerator::FinishInitialization();
+  variables_["array_comment"] =
+      "// |" + variables_["name"] + "| contains |" + variables_["storage_type"] + "|\n";
+}
+
 void RepeatedEnumFieldGenerator::GenerateFieldDescriptionTypeSpecific(
     io::Printer* printer) const {
   printer->Print(
diff --git a/src/google/protobuf/compiler/objectivec/objectivec_enum_field.h b/src/google/protobuf/compiler/objectivec/objectivec_enum_field.h
index b629eae..ae2f57e 100644
--- a/src/google/protobuf/compiler/objectivec/objectivec_enum_field.h
+++ b/src/google/protobuf/compiler/objectivec/objectivec_enum_field.h
@@ -41,7 +41,8 @@
 namespace objectivec {
 
 class EnumFieldGenerator : public SingleFieldGenerator {
-  friend FieldGenerator* FieldGenerator::Make(const FieldDescriptor* field);
+  friend FieldGenerator* FieldGenerator::Make(const FieldDescriptor* field,
+                                              const Options& options);
 
  public:
   virtual void GenerateFieldDescriptionTypeSpecific(io::Printer* printer) const;
@@ -50,7 +51,7 @@
   virtual void DetermineForwardDeclarations(set<string>* fwd_decls) const;
 
  protected:
-  explicit EnumFieldGenerator(const FieldDescriptor* descriptor);
+  EnumFieldGenerator(const FieldDescriptor* descriptor, const Options& options);
   virtual ~EnumFieldGenerator();
 
  private:
@@ -58,13 +59,16 @@
 };
 
 class RepeatedEnumFieldGenerator : public RepeatedFieldGenerator {
-  friend FieldGenerator* FieldGenerator::Make(const FieldDescriptor* field);
+  friend FieldGenerator* FieldGenerator::Make(const FieldDescriptor* field,
+                                              const Options& options);
 
  public:
+  virtual void FinishInitialization();
   virtual void GenerateFieldDescriptionTypeSpecific(io::Printer* printer) const;
 
  protected:
-  RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor);
+  RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor,
+                             const Options& options);
   virtual ~RepeatedEnumFieldGenerator();
 
  private:
diff --git a/src/google/protobuf/compiler/objectivec/objectivec_field.cc b/src/google/protobuf/compiler/objectivec/objectivec_field.cc
index cf5d8cf..0934182 100644
--- a/src/google/protobuf/compiler/objectivec/objectivec_field.cc
+++ b/src/google/protobuf/compiler/objectivec/objectivec_field.cc
@@ -45,6 +45,7 @@
 namespace objectivec {
 
 namespace {
+
 void SetCommonFieldVariables(const FieldDescriptor* descriptor,
                              map<string, string>* variables) {
   string camel_case_name = FieldName(descriptor);
@@ -117,39 +118,40 @@
 
 }  // namespace
 
-FieldGenerator* FieldGenerator::Make(const FieldDescriptor* field) {
+FieldGenerator* FieldGenerator::Make(const FieldDescriptor* field,
+                                     const Options& options) {
   FieldGenerator* result = NULL;
   if (field->is_repeated()) {
     switch (GetObjectiveCType(field)) {
       case OBJECTIVECTYPE_MESSAGE: {
         if (field->is_map()) {
-          result = new MapFieldGenerator(field);
+          result = new MapFieldGenerator(field, options);
         } else {
-          result = new RepeatedMessageFieldGenerator(field);
+          result = new RepeatedMessageFieldGenerator(field, options);
         }
         break;
       }
       case OBJECTIVECTYPE_ENUM:
-        result = new RepeatedEnumFieldGenerator(field);
+        result = new RepeatedEnumFieldGenerator(field, options);
         break;
       default:
-        result = new RepeatedPrimitiveFieldGenerator(field);
+        result = new RepeatedPrimitiveFieldGenerator(field, options);
         break;
     }
   } else {
     switch (GetObjectiveCType(field)) {
       case OBJECTIVECTYPE_MESSAGE: {
-        result = new MessageFieldGenerator(field);
+        result = new MessageFieldGenerator(field, options);
         break;
       }
       case OBJECTIVECTYPE_ENUM:
-        result = new EnumFieldGenerator(field);
+        result = new EnumFieldGenerator(field, options);
         break;
       default:
         if (IsReferenceType(field)) {
-          result = new PrimitiveObjFieldGenerator(field);
+          result = new PrimitiveObjFieldGenerator(field, options);
         } else {
-          result = new PrimitiveFieldGenerator(field);
+          result = new PrimitiveFieldGenerator(field, options);
         }
         break;
     }
@@ -158,8 +160,8 @@
   return result;
 }
 
-
-FieldGenerator::FieldGenerator(const FieldDescriptor* descriptor)
+FieldGenerator::FieldGenerator(const FieldDescriptor* descriptor,
+                               const Options& options)
     : descriptor_(descriptor) {
   SetCommonFieldVariables(descriptor, &variables_);
 }
@@ -252,9 +254,9 @@
   }
 }
 
-SingleFieldGenerator::SingleFieldGenerator(
-    const FieldDescriptor* descriptor)
-    : FieldGenerator(descriptor) {
+SingleFieldGenerator::SingleFieldGenerator(const FieldDescriptor* descriptor,
+                                           const Options& options)
+    : FieldGenerator(descriptor, options) {
   // Nothing
 }
 
@@ -300,9 +302,9 @@
   return false;
 }
 
-ObjCObjFieldGenerator::ObjCObjFieldGenerator(
-    const FieldDescriptor* descriptor)
-    : SingleFieldGenerator(descriptor) {
+ObjCObjFieldGenerator::ObjCObjFieldGenerator(const FieldDescriptor* descriptor,
+                                             const Options& options)
+    : SingleFieldGenerator(descriptor, options) {
   variables_["property_storage_attribute"] = "strong";
   if (IsRetainedName(variables_["name"])) {
     variables_["storage_attribute"] = " NS_RETURNS_NOT_RETAINED";
@@ -342,18 +344,21 @@
 }
 
 RepeatedFieldGenerator::RepeatedFieldGenerator(
-    const FieldDescriptor* descriptor)
-    : ObjCObjFieldGenerator(descriptor) {
+    const FieldDescriptor* descriptor, const Options& options)
+    : ObjCObjFieldGenerator(descriptor, options) {
   // Repeated fields don't use the has index.
   variables_["has_index"] = "GPBNoHasBit";
+  // Default to no comment and let the cases needing it fill it in.
+  variables_["array_comment"] = "";
 }
 
 RepeatedFieldGenerator::~RepeatedFieldGenerator() {}
 
 void RepeatedFieldGenerator::FinishInitialization(void) {
   FieldGenerator::FinishInitialization();
-  variables_["array_comment"] =
-      "// |" + variables_["name"] + "| contains |" + variables_["storage_type"] + "|\n";
+  if (variables_.find("array_property_type") == variables_.end()) {
+    variables_["array_property_type"] = variable("array_storage_type");
+  }
 }
 
 void RepeatedFieldGenerator::GenerateFieldStorageDeclaration(
@@ -379,13 +384,13 @@
       variables_,
       "$comments$"
       "$array_comment$"
-      "@property(nonatomic, readwrite, strong, null_resettable) $array_storage_type$ *$name$$storage_attribute$;\n"
+      "@property(nonatomic, readwrite, strong, null_resettable) $array_property_type$ *$name$$storage_attribute$;\n"
       "@property(nonatomic, readonly) NSUInteger $name$_Count;\n");
   if (IsInitName(variables_.find("name")->second)) {
     // If property name starts with init we need to annotate it to get past ARC.
     // http://stackoverflow.com/questions/18723226/how-do-i-annotate-an-objective-c-property-with-an-objc-method-family/18723227#18723227
     printer->Print(variables_,
-                   "- ($array_storage_type$ *)$name$ GPB_METHOD_FAMILY_NONE;\n");
+                   "- ($array_property_type$ *)$name$ GPB_METHOD_FAMILY_NONE;\n");
   }
   printer->Print("\n");
 }
@@ -395,7 +400,8 @@
   return false;
 }
 
-FieldGeneratorMap::FieldGeneratorMap(const Descriptor* descriptor)
+FieldGeneratorMap::FieldGeneratorMap(const Descriptor* descriptor,
+                                     const Options& options)
     : descriptor_(descriptor),
       field_generators_(
           new scoped_ptr<FieldGenerator>[descriptor->field_count()]),
@@ -403,10 +409,12 @@
           new scoped_ptr<FieldGenerator>[descriptor->extension_count()]) {
   // Construct all the FieldGenerators.
   for (int i = 0; i < descriptor->field_count(); i++) {
-    field_generators_[i].reset(FieldGenerator::Make(descriptor->field(i)));
+    field_generators_[i].reset(
+        FieldGenerator::Make(descriptor->field(i), options));
   }
   for (int i = 0; i < descriptor->extension_count(); i++) {
-    extension_generators_[i].reset(FieldGenerator::Make(descriptor->extension(i)));
+    extension_generators_[i].reset(
+        FieldGenerator::Make(descriptor->extension(i), options));
   }
 }
 
diff --git a/src/google/protobuf/compiler/objectivec/objectivec_field.h b/src/google/protobuf/compiler/objectivec/objectivec_field.h
index 130a52d..e8a20a7 100644
--- a/src/google/protobuf/compiler/objectivec/objectivec_field.h
+++ b/src/google/protobuf/compiler/objectivec/objectivec_field.h
@@ -49,24 +49,31 @@
 
 class FieldGenerator {
  public:
-  static FieldGenerator* Make(const FieldDescriptor* field);
+  static FieldGenerator* Make(const FieldDescriptor* field,
+                              const Options& options);
 
   virtual ~FieldGenerator();
 
+  // Exposed for subclasses to fill in.
   virtual void GenerateFieldStorageDeclaration(io::Printer* printer) const = 0;
   virtual void GeneratePropertyDeclaration(io::Printer* printer) const = 0;
-
   virtual void GeneratePropertyImplementation(io::Printer* printer) const = 0;
 
-  virtual void GenerateFieldDescription(io::Printer* printer) const;
+  // Called by GenerateFieldDescription, exposed for classes that need custom
+  // generation.
   virtual void GenerateFieldDescriptionTypeSpecific(io::Printer* printer) const;
-  virtual void GenerateFieldNumberConstant(io::Printer* printer) const;
 
+  // Exposed for subclasses to extend, base does nothing.
   virtual void GenerateCFunctionDeclarations(io::Printer* printer) const;
   virtual void GenerateCFunctionImplementations(io::Printer* printer) const;
 
+  // Exposed for subclasses, should always call it on the parent class also.
   virtual void DetermineForwardDeclarations(set<string>* fwd_decls) const;
 
+  // Used during generation, not intended to be extended by subclasses.
+  void GenerateFieldDescription(io::Printer* printer) const;
+  void GenerateFieldNumberConstant(io::Printer* printer) const;
+
   void SetOneofIndexBase(int index_base);
 
   string variable(const char* key) const {
@@ -81,7 +88,7 @@
   string raw_field_name() const { return variable("raw_field_name"); }
 
  protected:
-  explicit FieldGenerator(const FieldDescriptor* descriptor);
+  FieldGenerator(const FieldDescriptor* descriptor, const Options& options);
 
   virtual void FinishInitialization(void);
   virtual bool WantsHasProperty(void) const = 0;
@@ -103,7 +110,8 @@
   virtual void GeneratePropertyImplementation(io::Printer* printer) const;
 
  protected:
-  explicit SingleFieldGenerator(const FieldDescriptor* descriptor);
+  SingleFieldGenerator(const FieldDescriptor* descriptor,
+                       const Options& options);
   virtual bool WantsHasProperty(void) const;
 
  private:
@@ -119,7 +127,8 @@
   virtual void GeneratePropertyDeclaration(io::Printer* printer) const;
 
  protected:
-  explicit ObjCObjFieldGenerator(const FieldDescriptor* descriptor);
+  ObjCObjFieldGenerator(const FieldDescriptor* descriptor,
+                        const Options& options);
 
  private:
   GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ObjCObjFieldGenerator);
@@ -135,7 +144,8 @@
   virtual void GeneratePropertyImplementation(io::Printer* printer) const;
 
  protected:
-  explicit RepeatedFieldGenerator(const FieldDescriptor* descriptor);
+  RepeatedFieldGenerator(const FieldDescriptor* descriptor,
+                         const Options& options);
   virtual void FinishInitialization(void);
   virtual bool WantsHasProperty(void) const;
 
@@ -146,7 +156,7 @@
 // Convenience class which constructs FieldGenerators for a Descriptor.
 class FieldGeneratorMap {
  public:
-  explicit FieldGeneratorMap(const Descriptor* descriptor);
+  FieldGeneratorMap(const Descriptor* descriptor, const Options& options);
   ~FieldGeneratorMap();
 
   const FieldGenerator& get(const FieldDescriptor* field) const;
diff --git a/src/google/protobuf/compiler/objectivec/objectivec_file.cc b/src/google/protobuf/compiler/objectivec/objectivec_file.cc
index 228c66f..cdf9ebb 100644
--- a/src/google/protobuf/compiler/objectivec/objectivec_file.cc
+++ b/src/google/protobuf/compiler/objectivec/objectivec_file.cc
@@ -50,17 +50,18 @@
 namespace compiler {
 namespace objectivec {
 
-FileGenerator::FileGenerator(const FileDescriptor *file)
+FileGenerator::FileGenerator(const FileDescriptor *file, const Options& options)
     : file_(file),
       root_class_name_(FileClassName(file)),
-      is_public_dep_(false) {
+      is_public_dep_(false),
+      options_(options) {
   for (int i = 0; i < file_->enum_type_count(); i++) {
     EnumGenerator *generator = new EnumGenerator(file_->enum_type(i));
     enum_generators_.push_back(generator);
   }
   for (int i = 0; i < file_->message_type_count(); i++) {
     MessageGenerator *generator =
-        new MessageGenerator(root_class_name_, file_->message_type(i));
+        new MessageGenerator(root_class_name_, file_->message_type(i), options_);
     message_generators_.push_back(generator);
   }
   for (int i = 0; i < file_->extension_count(); i++) {
@@ -352,7 +353,8 @@
       public_import_names.insert(file_->public_dependency(i)->name());
     }
     for (int i = 0; i < file_->dependency_count(); i++) {
-      FileGenerator *generator = new FileGenerator(file_->dependency(i));
+      FileGenerator *generator =
+          new FileGenerator(file_->dependency(i), options_);
       const string& name = file_->dependency(i)->name();
       bool public_import = (public_import_names.count(name) != 0);
       generator->SetIsPublicDependency(public_import);
diff --git a/src/google/protobuf/compiler/objectivec/objectivec_file.h b/src/google/protobuf/compiler/objectivec/objectivec_file.h
index 1bb4f0e..4c0fcd3 100644
--- a/src/google/protobuf/compiler/objectivec/objectivec_file.h
+++ b/src/google/protobuf/compiler/objectivec/objectivec_file.h
@@ -55,7 +55,7 @@
 
 class FileGenerator {
  public:
-  explicit FileGenerator(const FileDescriptor* file);
+  FileGenerator(const FileDescriptor* file, const Options& options);
   ~FileGenerator();
 
   void GenerateSource(io::Printer* printer);
@@ -84,6 +84,8 @@
   vector<ExtensionGenerator*> extension_generators_;
   bool is_public_dep_;
 
+  const Options options_;
+
   const vector<FileGenerator*>& DependencyGenerators();
 
   GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FileGenerator);
diff --git a/src/google/protobuf/compiler/objectivec/objectivec_generator.cc b/src/google/protobuf/compiler/objectivec/objectivec_generator.cc
index 375b4e0..72e295d 100644
--- a/src/google/protobuf/compiler/objectivec/objectivec_generator.cc
+++ b/src/google/protobuf/compiler/objectivec/objectivec_generator.cc
@@ -49,21 +49,31 @@
                                    const string& parameter,
                                    OutputDirectory* output_directory,
                                    string* error) const {
-  // ObjC doesn't have any options at the moment, error if passed one.
+  // -----------------------------------------------------------------
+  // Parse generator options.
+
+  Options generation_options;
+
   vector<pair<string, string> > options;
   ParseGeneratorParameter(parameter, &options);
   for (int i = 0; i < options.size(); i++) {
-    *error = "error:: Unknown generator option: " + options[i].first;
-    return false;
+    if (options[i].first == "expected_prefixes_path") {
+      generation_options.expected_prefixes_path = options[i].second;
+    } else {
+      *error = "error: Unknown generator option: " + options[i].first;
+      return false;
+    }
   }
 
+  // -----------------------------------------------------------------
+
   // Validate the objc prefix/package pairing.
-  if (!ValidateObjCClassPrefix(file, error)) {
+  if (!ValidateObjCClassPrefix(file, generation_options, error)) {
     // *error will have been filled in.
     return false;
   }
 
-  FileGenerator file_generator(file);
+  FileGenerator file_generator(file, generation_options);
   string filepath = FilePath(file);
 
   // Generate header.
diff --git a/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc b/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc
index 8527b74..77a378c 100644
--- a/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc
+++ b/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc
@@ -58,6 +58,14 @@
 namespace compiler {
 namespace objectivec {
 
+Options::Options() {
+  // Default is the value of the env for the package prefixes.
+  const char* file_path = getenv("GPB_OBJC_EXPECTED_PACKAGE_PREFIXES");
+  if (file_path) {
+    expected_prefixes_path = file_path;
+  }
+}
+
 namespace {
 
 hash_set<string> MakeWordsMap(const char* const words[], size_t num_words) {
@@ -890,26 +898,26 @@
   return true;
 }
 
-bool LoadExpectedPackagePrefixes(map<string, string>* prefix_map,
-                                 string* out_expect_file_path,
+bool LoadExpectedPackagePrefixes(const Options &generation_options,
+                                 map<string, string>* prefix_map,
                                  string* out_error) {
-  const char* file_path = getenv("GPB_OBJC_EXPECTED_PACKAGE_PREFIXES");
-  if (file_path == NULL) {
+  if (generation_options.expected_prefixes_path.empty()) {
     return true;
   }
 
   int fd;
   do {
-    fd = open(file_path, O_RDONLY);
+    fd = open(generation_options.expected_prefixes_path.c_str(), O_RDONLY);
   } while (fd < 0 && errno == EINTR);
   if (fd < 0) {
     *out_error =
-        string(file_path) + ":0:0: error: Unable to open." + strerror(errno);
+        string("error: Unable to open \"") +
+        generation_options.expected_prefixes_path +
+        "\", " + strerror(errno);
     return false;
   }
   io::FileInputStream file_stream(fd);
   file_stream.SetCloseOnDelete(true);
-  *out_expect_file_path = file_path;
 
   Parser parser(prefix_map);
   const void* buf;
@@ -920,8 +928,9 @@
     }
 
     if (!parser.ParseChunk(StringPiece(static_cast<const char*>(buf), buf_len))) {
-      *out_error = string(file_path) + ":" + SimpleItoa(parser.last_line()) +
-                   ":0: error: " + parser.error_str();
+      *out_error =
+          string("error: ") + generation_options.expected_prefixes_path +
+          " Line " + SimpleItoa(parser.last_line()) + ", " + parser.error_str();
       return false;
     }
   }
@@ -930,7 +939,9 @@
 
 }  // namespace
 
-bool ValidateObjCClassPrefix(const FileDescriptor* file, string* out_error) {
+bool ValidateObjCClassPrefix(const FileDescriptor* file,
+                             const Options& generation_options,
+                             string* out_error) {
   const string prefix = file->options().objc_class_prefix();
   const string package = file->package();
 
@@ -939,11 +950,10 @@
 
   // Load any expected package prefixes to validate against those.
   map<string, string> expected_package_prefixes;
-  string expect_file_path;
-  if (!LoadExpectedPackagePrefixes(&expected_package_prefixes,
-                                   &expect_file_path, out_error)) {
-    // Any error, clear the entries that were read.
-    expected_package_prefixes.clear();
+  if (!LoadExpectedPackagePrefixes(generation_options,
+                                   &expected_package_prefixes,
+                                   out_error)) {
+    return false;
   }
 
   // Check: Error - See if there was an expected prefix for the package and
@@ -957,7 +967,7 @@
       return true;
     } else {
       // ...it didn't match!
-      *out_error = "protoc:0: error: Expected 'option objc_class_prefix = \"" +
+      *out_error = "error: Expected 'option objc_class_prefix = \"" +
                    package_match->second + "\";' in '" + file->name() + "'";
       if (prefix.length()) {
         *out_error += "; but found '" + prefix + "' instead";
@@ -980,11 +990,11 @@
        i != expected_package_prefixes.end(); ++i) {
     if (i->second == prefix) {
       *out_error =
-          "protoc:0: error: Found 'option objc_class_prefix = \"" + prefix +
+          "error: Found 'option objc_class_prefix = \"" + prefix +
           "\";' in '" + file->name() +
           "'; that prefix is already used for 'package " + i->first +
           ";'. It can only be reused by listing it in the expected file (" +
-          expect_file_path + ").";
+          generation_options.expected_prefixes_path + ").";
       return false;  // Only report first usage of the prefix.
     }
   }
@@ -1017,7 +1027,7 @@
          << "protoc:0: warning: Found unexpected 'option objc_class_prefix = \""
          << prefix << "\";' in '" << file->name() << "';"
          << " consider adding it to the expected prefixes file ("
-         << expect_file_path << ")." << endl;
+         << generation_options.expected_prefixes_path << ")." << endl;
     cerr.flush();
   }
 
diff --git a/src/google/protobuf/compiler/objectivec/objectivec_helpers.h b/src/google/protobuf/compiler/objectivec/objectivec_helpers.h
index 8574486..5b2dd19 100644
--- a/src/google/protobuf/compiler/objectivec/objectivec_helpers.h
+++ b/src/google/protobuf/compiler/objectivec/objectivec_helpers.h
@@ -42,6 +42,12 @@
 namespace compiler {
 namespace objectivec {
 
+// Generator options (see objectivec_generator.cc for a description of each):
+struct Options {
+  Options();
+  string expected_prefixes_path;
+};
+
 // Strips ".proto" or ".protodevel" from the end of a filename.
 string StripProto(const string& filename);
 
@@ -145,7 +151,9 @@
 // Checks the prefix for a given file and outputs any warnings needed, if
 // there are flat out errors, then out_error is filled in and the result is
 // false.
-bool ValidateObjCClassPrefix(const FileDescriptor* file, string *out_error);
+bool ValidateObjCClassPrefix(const FileDescriptor* file,
+                             const Options& generation_options,
+                             string* out_error);
 
 // Generate decode data needed for ObjC's GPBDecodeTextFormatName() to transform
 // the input into the expected output.
diff --git a/src/google/protobuf/compiler/objectivec/objectivec_map_field.cc b/src/google/protobuf/compiler/objectivec/objectivec_map_field.cc
index 2987f3d..2751e93 100644
--- a/src/google/protobuf/compiler/objectivec/objectivec_map_field.cc
+++ b/src/google/protobuf/compiler/objectivec/objectivec_map_field.cc
@@ -84,13 +84,14 @@
 
 }  // namespace
 
-MapFieldGenerator::MapFieldGenerator(const FieldDescriptor* descriptor)
-    : RepeatedFieldGenerator(descriptor) {
+MapFieldGenerator::MapFieldGenerator(const FieldDescriptor* descriptor,
+                                     const Options& options)
+    : RepeatedFieldGenerator(descriptor, options) {
   const FieldDescriptor* key_descriptor =
       descriptor->message_type()->FindFieldByName("key");
   const FieldDescriptor* value_descriptor =
       descriptor->message_type()->FindFieldByName("value");
-  value_field_generator_.reset(FieldGenerator::Make(value_descriptor));
+  value_field_generator_.reset(FieldGenerator::Make(value_descriptor, options));
 
   // Pull over some variables_ from the value.
   variables_["field_type"] = value_field_generator_->variable("field_type");
@@ -117,16 +118,27 @@
   variables_["fieldflags"] = BuildFlagsString(field_flags);
 
   ObjectiveCType value_objc_type = GetObjectiveCType(value_descriptor);
-  if ((GetObjectiveCType(key_descriptor) == OBJECTIVECTYPE_STRING) &&
+  const bool value_is_object_type =
       ((value_objc_type == OBJECTIVECTYPE_STRING) ||
        (value_objc_type == OBJECTIVECTYPE_DATA) ||
-       (value_objc_type == OBJECTIVECTYPE_MESSAGE))) {
+       (value_objc_type == OBJECTIVECTYPE_MESSAGE));
+  if ((GetObjectiveCType(key_descriptor) == OBJECTIVECTYPE_STRING) &&
+      value_is_object_type) {
     variables_["array_storage_type"] = "NSMutableDictionary";
+    variables_["array_property_type"] =
+        "NSMutableDictionary<NSString*, " +
+        value_field_generator_->variable("storage_type") + "*>";
   } else {
-    string base_name = MapEntryTypeName(key_descriptor, true);
-    base_name += MapEntryTypeName(value_descriptor, false);
-    base_name += "Dictionary";
-    variables_["array_storage_type"] = "GPB" + base_name;
+    string class_name("GPB");
+    class_name += MapEntryTypeName(key_descriptor, true);
+    class_name += MapEntryTypeName(value_descriptor, false);
+    class_name += "Dictionary";
+    variables_["array_storage_type"] = class_name;
+    if (value_is_object_type) {
+      variables_["array_property_type"] =
+          class_name + "<" +
+          value_field_generator_->variable("storage_type") + "*>";
+    }
   }
 }
 
@@ -138,15 +150,9 @@
   // values in the map are.
   const FieldDescriptor* value_descriptor =
       descriptor_->message_type()->FindFieldByName("value");
-  ObjectiveCType value_objc_type = GetObjectiveCType(value_descriptor);
-  if ((value_objc_type == OBJECTIVECTYPE_MESSAGE) ||
-      (value_objc_type == OBJECTIVECTYPE_DATA) ||
-      (value_objc_type == OBJECTIVECTYPE_STRING) ||
-      (value_objc_type == OBJECTIVECTYPE_ENUM)) {
+  if (GetObjectiveCType(value_descriptor) == OBJECTIVECTYPE_ENUM) {
     variables_["array_comment"] =
         "// |" + variables_["name"] + "| values are |" + value_field_generator_->variable("storage_type") + "|\n";
-  } else {
-    variables_["array_comment"] = "";
   }
 }
 
@@ -157,6 +163,19 @@
   value_field_generator_->GenerateFieldDescriptionTypeSpecific(printer);
 }
 
+void MapFieldGenerator::DetermineForwardDeclarations(
+    set<string>* fwd_decls) const {
+  RepeatedFieldGenerator::DetermineForwardDeclarations(fwd_decls);
+  const FieldDescriptor* value_descriptor =
+      descriptor_->message_type()->FindFieldByName("value");
+  if (GetObjectiveCType(value_descriptor) == OBJECTIVECTYPE_MESSAGE) {
+    const string& value_storage_type =
+        value_field_generator_->variable("storage_type");
+    fwd_decls->insert("@class " + value_storage_type);
+  }
+}
+
+
 }  // namespace objectivec
 }  // namespace compiler
 }  // namespace protobuf
diff --git a/src/google/protobuf/compiler/objectivec/objectivec_map_field.h b/src/google/protobuf/compiler/objectivec/objectivec_map_field.h
index 173541f..7351ea0 100644
--- a/src/google/protobuf/compiler/objectivec/objectivec_map_field.h
+++ b/src/google/protobuf/compiler/objectivec/objectivec_map_field.h
@@ -41,18 +41,22 @@
 namespace objectivec {
 
 class MapFieldGenerator : public RepeatedFieldGenerator {
-  friend FieldGenerator* FieldGenerator::Make(const FieldDescriptor* field);
+  friend FieldGenerator* FieldGenerator::Make(const FieldDescriptor* field,
+                                              const Options& options);
 
  public:
   virtual void FinishInitialization(void);
   virtual void GenerateFieldDescriptionTypeSpecific(io::Printer* printer) const;
 
  protected:
-  explicit MapFieldGenerator(const FieldDescriptor* descriptor);
+  MapFieldGenerator(const FieldDescriptor* descriptor, const Options& options);
   virtual ~MapFieldGenerator();
 
+  virtual void DetermineForwardDeclarations(set<string>* fwd_decls) const;
+
  private:
   scoped_ptr<FieldGenerator> value_field_generator_;
+
   GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MapFieldGenerator);
 };
 
diff --git a/src/google/protobuf/compiler/objectivec/objectivec_message.cc b/src/google/protobuf/compiler/objectivec/objectivec_message.cc
index 32671d4..e0ea8bd 100644
--- a/src/google/protobuf/compiler/objectivec/objectivec_message.cc
+++ b/src/google/protobuf/compiler/objectivec/objectivec_message.cc
@@ -174,10 +174,11 @@
 }  // namespace
 
 MessageGenerator::MessageGenerator(const string& root_classname,
-                                   const Descriptor* descriptor)
+                                   const Descriptor* descriptor,
+                                   const Options& options)
     : root_classname_(root_classname),
       descriptor_(descriptor),
-      field_generators_(descriptor),
+      field_generators_(descriptor, options),
       class_name_(ClassName(descriptor_)) {
   for (int i = 0; i < descriptor_->extension_count(); i++) {
     extension_generators_.push_back(
@@ -196,7 +197,9 @@
 
   for (int i = 0; i < descriptor_->nested_type_count(); i++) {
     MessageGenerator* generator =
-        new MessageGenerator(root_classname_, descriptor_->nested_type(i));
+        new MessageGenerator(root_classname_,
+                             descriptor_->nested_type(i),
+                             options);
     nested_message_generators_.push_back(generator);
   }
 }
@@ -230,11 +233,6 @@
   if (!IsMapEntryMessage(descriptor_)) {
     for (int i = 0; i < descriptor_->field_count(); i++) {
       const FieldDescriptor* fieldDescriptor = descriptor_->field(i);
-      // If it is a the field is repeated, the type will be and *Array, and we
-      // don't need any forward decl.
-      if (fieldDescriptor->is_repeated()) {
-        continue;
-      }
       field_generators_.get(fieldDescriptor)
           .DetermineForwardDeclarations(fwd_decls);
     }
diff --git a/src/google/protobuf/compiler/objectivec/objectivec_message.h b/src/google/protobuf/compiler/objectivec/objectivec_message.h
index 06b536f..8565e76 100644
--- a/src/google/protobuf/compiler/objectivec/objectivec_message.h
+++ b/src/google/protobuf/compiler/objectivec/objectivec_message.h
@@ -54,7 +54,9 @@
 
 class MessageGenerator {
  public:
-  MessageGenerator(const string& root_classname, const Descriptor* descriptor);
+  MessageGenerator(const string& root_classname,
+                   const Descriptor* descriptor,
+                   const Options& options);
   ~MessageGenerator();
 
   void GenerateStaticVariablesInitialization(io::Printer* printer);
diff --git a/src/google/protobuf/compiler/objectivec/objectivec_message_field.cc b/src/google/protobuf/compiler/objectivec/objectivec_message_field.cc
index f2ce4e5..d6ccd6d 100644
--- a/src/google/protobuf/compiler/objectivec/objectivec_message_field.cc
+++ b/src/google/protobuf/compiler/objectivec/objectivec_message_field.cc
@@ -58,8 +58,9 @@
 
 }  // namespace
 
-MessageFieldGenerator::MessageFieldGenerator(const FieldDescriptor* descriptor)
-    : ObjCObjFieldGenerator(descriptor) {
+MessageFieldGenerator::MessageFieldGenerator(const FieldDescriptor* descriptor,
+                                             const Options& options)
+    : ObjCObjFieldGenerator(descriptor, options) {
   SetMessageVariables(descriptor, &variables_);
 }
 
@@ -67,6 +68,7 @@
 
 void MessageFieldGenerator::DetermineForwardDeclarations(
     set<string>* fwd_decls) const {
+  ObjCObjFieldGenerator::DetermineForwardDeclarations(fwd_decls);
   // Class name is already in "storage_type".
   fwd_decls->insert("@class " + variable("storage_type"));
 }
@@ -82,14 +84,24 @@
 }
 
 RepeatedMessageFieldGenerator::RepeatedMessageFieldGenerator(
-    const FieldDescriptor* descriptor)
-    : RepeatedFieldGenerator(descriptor) {
+    const FieldDescriptor* descriptor, const Options& options)
+    : RepeatedFieldGenerator(descriptor, options) {
   SetMessageVariables(descriptor, &variables_);
   variables_["array_storage_type"] = "NSMutableArray";
+  variables_["array_property_type"] =
+      "NSMutableArray<" + variables_["storage_type"] + "*>";
 }
 
 RepeatedMessageFieldGenerator::~RepeatedMessageFieldGenerator() {}
 
+void RepeatedMessageFieldGenerator::DetermineForwardDeclarations(
+    set<string>* fwd_decls) const {
+  RepeatedFieldGenerator::DetermineForwardDeclarations(fwd_decls);
+  // Class name is already in "storage_type".
+  fwd_decls->insert("@class " + variable("storage_type"));
+}
+
+
 }  // namespace objectivec
 }  // namespace compiler
 }  // namespace protobuf
diff --git a/src/google/protobuf/compiler/objectivec/objectivec_message_field.h b/src/google/protobuf/compiler/objectivec/objectivec_message_field.h
index 708ea56..d2dba15 100644
--- a/src/google/protobuf/compiler/objectivec/objectivec_message_field.h
+++ b/src/google/protobuf/compiler/objectivec/objectivec_message_field.h
@@ -41,10 +41,12 @@
 namespace objectivec {
 
 class MessageFieldGenerator : public ObjCObjFieldGenerator {
-  friend FieldGenerator* FieldGenerator::Make(const FieldDescriptor* field);
+  friend FieldGenerator* FieldGenerator::Make(const FieldDescriptor* field,
+                                              const Options& options);
 
  protected:
-  explicit MessageFieldGenerator(const FieldDescriptor* descriptor);
+  MessageFieldGenerator(const FieldDescriptor* descriptor,
+                        const Options& options);
   virtual ~MessageFieldGenerator();
   virtual bool WantsHasProperty(void) const;
 
@@ -56,12 +58,17 @@
 };
 
 class RepeatedMessageFieldGenerator : public RepeatedFieldGenerator {
-  friend FieldGenerator* FieldGenerator::Make(const FieldDescriptor* field);
+  friend FieldGenerator* FieldGenerator::Make(const FieldDescriptor* field,
+                                              const Options& options);
 
  protected:
-  explicit RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor);
+  RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor,
+                                const Options& options);
   virtual ~RepeatedMessageFieldGenerator();
 
+ public:
+  virtual void DetermineForwardDeclarations(set<string>* fwd_decls) const;
+
  private:
   GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedMessageFieldGenerator);
 };
diff --git a/src/google/protobuf/compiler/objectivec/objectivec_primitive_field.cc b/src/google/protobuf/compiler/objectivec/objectivec_primitive_field.cc
index c185b66..ea7f1b9 100644
--- a/src/google/protobuf/compiler/objectivec/objectivec_primitive_field.cc
+++ b/src/google/protobuf/compiler/objectivec/objectivec_primitive_field.cc
@@ -74,7 +74,7 @@
     case OBJECTIVECTYPE_ENUM:
       return "int32_t";
     case OBJECTIVECTYPE_MESSAGE:
-      return NULL;
+      return NULL;  // Messages go through objectivec_message_field.cc|h.
   }
 
   // Some compilers report reaching end of function even though all cases of
@@ -107,7 +107,8 @@
     case OBJECTIVECTYPE_ENUM:
       return "Enum";
     case OBJECTIVECTYPE_MESSAGE:
-      return "";  // Want NSArray
+      // Want NSArray (but goes through objectivec_message_field.cc|h).
+      return "";
   }
 
   // Some compilers report reaching end of function even though all cases of
@@ -126,16 +127,16 @@
 }  // namespace
 
 PrimitiveFieldGenerator::PrimitiveFieldGenerator(
-    const FieldDescriptor* descriptor)
-    : SingleFieldGenerator(descriptor) {
+    const FieldDescriptor* descriptor, const Options& options)
+    : SingleFieldGenerator(descriptor, options) {
   SetPrimitiveVariables(descriptor, &variables_);
 }
 
 PrimitiveFieldGenerator::~PrimitiveFieldGenerator() {}
 
 PrimitiveObjFieldGenerator::PrimitiveObjFieldGenerator(
-    const FieldDescriptor* descriptor)
-    : ObjCObjFieldGenerator(descriptor) {
+    const FieldDescriptor* descriptor, const Options& options)
+    : ObjCObjFieldGenerator(descriptor, options) {
   SetPrimitiveVariables(descriptor, &variables_);
   variables_["property_storage_attribute"] = "copy";
 }
@@ -143,8 +144,8 @@
 PrimitiveObjFieldGenerator::~PrimitiveObjFieldGenerator() {}
 
 RepeatedPrimitiveFieldGenerator::RepeatedPrimitiveFieldGenerator(
-    const FieldDescriptor* descriptor)
-    : RepeatedFieldGenerator(descriptor) {
+    const FieldDescriptor* descriptor, const Options& options)
+    : RepeatedFieldGenerator(descriptor, options) {
   SetPrimitiveVariables(descriptor, &variables_);
 
   string base_name = PrimitiveArrayTypeName(descriptor);
@@ -152,19 +153,13 @@
     variables_["array_storage_type"] = "GPB" + base_name + "Array";
   } else {
     variables_["array_storage_type"] = "NSMutableArray";
+    variables_["array_property_type"] =
+        "NSMutableArray<" + variables_["storage_type"] + "*>";
   }
 }
 
 RepeatedPrimitiveFieldGenerator::~RepeatedPrimitiveFieldGenerator() {}
 
-void RepeatedPrimitiveFieldGenerator::FinishInitialization(void) {
-  RepeatedFieldGenerator::FinishInitialization();
-  if (IsPrimitiveType(descriptor_)) {
-    // No comment needed for primitive types.
-    variables_["array_comment"] = "";
-  }
-}
-
 }  // namespace objectivec
 }  // namespace compiler
 }  // namespace protobuf
diff --git a/src/google/protobuf/compiler/objectivec/objectivec_primitive_field.h b/src/google/protobuf/compiler/objectivec/objectivec_primitive_field.h
index 9bb7934..87139af 100644
--- a/src/google/protobuf/compiler/objectivec/objectivec_primitive_field.h
+++ b/src/google/protobuf/compiler/objectivec/objectivec_primitive_field.h
@@ -41,10 +41,12 @@
 namespace objectivec {
 
 class PrimitiveFieldGenerator : public SingleFieldGenerator {
-  friend FieldGenerator* FieldGenerator::Make(const FieldDescriptor* field);
+  friend FieldGenerator* FieldGenerator::Make(const FieldDescriptor* field,
+                                              const Options& options);
 
  protected:
-  explicit PrimitiveFieldGenerator(const FieldDescriptor* descriptor);
+  PrimitiveFieldGenerator(const FieldDescriptor* descriptor,
+                          const Options& options);
   virtual ~PrimitiveFieldGenerator();
 
  private:
@@ -52,10 +54,12 @@
 };
 
 class PrimitiveObjFieldGenerator : public ObjCObjFieldGenerator {
-  friend FieldGenerator* FieldGenerator::Make(const FieldDescriptor* field);
+  friend FieldGenerator* FieldGenerator::Make(const FieldDescriptor* field,
+                                              const Options& options);
 
  protected:
-  explicit PrimitiveObjFieldGenerator(const FieldDescriptor* descriptor);
+  PrimitiveObjFieldGenerator(const FieldDescriptor* descriptor,
+                             const Options& options);
   virtual ~PrimitiveObjFieldGenerator();
 
  private:
@@ -63,12 +67,13 @@
 };
 
 class RepeatedPrimitiveFieldGenerator : public RepeatedFieldGenerator {
-  friend FieldGenerator* FieldGenerator::Make(const FieldDescriptor* field);
+  friend FieldGenerator* FieldGenerator::Make(const FieldDescriptor* field,
+                                              const Options& options);
 
  protected:
-  explicit RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor);
+  RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor,
+                                  const Options& options);
   virtual ~RepeatedPrimitiveFieldGenerator();
-  virtual void FinishInitialization(void);
 
  private:
   GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedPrimitiveFieldGenerator);
diff --git a/src/google/protobuf/stubs/atomicops.h b/src/google/protobuf/stubs/atomicops.h
index cb93227..9b3d1e6 100644
--- a/src/google/protobuf/stubs/atomicops.h
+++ b/src/google/protobuf/stubs/atomicops.h
@@ -214,6 +214,8 @@
 #include <google/protobuf/stubs/atomicops_internals_power.h>
 #elif defined(__native_client__)
 #include <google/protobuf/stubs/atomicops_internals_pnacl.h>
+#elif defined(GOOGLE_PROTOBUF_ARCH_PPC)
+#include <google/protobuf/stubs/atomicops_internals_ppc_gcc.h>
 #elif (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4))
 #include <google/protobuf/stubs/atomicops_internals_generic_gcc.h>
 #elif defined(__clang__)
diff --git a/src/google/protobuf/stubs/atomicops_internals_ppc_gcc.h b/src/google/protobuf/stubs/atomicops_internals_ppc_gcc.h
new file mode 100644
index 0000000..8231a57
--- /dev/null
+++ b/src/google/protobuf/stubs/atomicops_internals_ppc_gcc.h
@@ -0,0 +1,155 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 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.
+
+// Author: ogabbay@advaoptical.com (Oded Gabbay)
+// Cleaned up by: bsilver16384@gmail.com (Brian Silverman)
+//
+// This file is an internal atomic implementation, use atomicops.h instead.
+
+#ifndef GOOGLE_PROTOBUF_ATOMICOPS_INTERNALS_PPC_GCC_H_
+#define GOOGLE_PROTOBUF_ATOMICOPS_INTERNALS_PPC_GCC_H_
+
+#define ATOMICOPS_COMPILER_BARRIER() __asm__ __volatile__("" : : : "memory")
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32 *ptr,
+                                         Atomic32 old_value,
+                                         Atomic32 new_value) {
+  Atomic32 prev;
+
+  __asm__ __volatile__(
+      "0:                                  \n\t"
+      "lwarx %[prev],0,%[ptr]              \n\t"
+      "cmpw 0,%[prev],%[old_value]         \n\t"
+      "bne- 1f                             \n\t"
+      "stwcx. %[new_value],0,%[ptr]        \n\t"
+      "bne- 0b                             \n\t"
+      "1:                                  \n\t"
+      : [prev] "=&r"(prev), "+m"(*ptr)
+      : [ptr] "r"(ptr), [old_value] "r"(old_value), [new_value] "r"(new_value)
+      : "cc", "memory");
+
+  return prev;
+}
+
+inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32 *ptr,
+                                         Atomic32 new_value) {
+  Atomic32 old;
+
+  __asm__ __volatile__(
+      "0:                                  \n\t"
+      "lwarx %[old],0,%[ptr]               \n\t"
+      "stwcx. %[new_value],0,%[ptr]        \n\t"
+      "bne- 0b                             \n\t"
+      : [old] "=&r"(old), "+m"(*ptr)
+      : [ptr] "r"(ptr), [new_value] "r"(new_value)
+      : "cc", "memory");
+
+  return old;
+}
+
+inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32 *ptr,
+                                          Atomic32 increment) {
+  Atomic32 temp;
+
+  __asm__ __volatile__(
+      "0:                                  \n\t"
+      "lwarx %[temp],0,%[ptr]              \n\t"
+      "add %[temp],%[increment],%[temp]    \n\t"
+      "stwcx. %[temp],0,%[ptr]             \n\t"
+      "bne- 0b                             \n\t"
+      : [temp] "=&r"(temp)
+      : [increment] "r"(increment), [ptr] "r"(ptr)
+      : "cc", "memory");
+
+  return temp;
+}
+
+inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32 *ptr,
+                                        Atomic32 increment) {
+  MemoryBarrier();
+  Atomic32 res = NoBarrier_AtomicIncrement(ptr, increment);
+  MemoryBarrier();
+  return res;
+}
+
+inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32 *ptr,
+                                       Atomic32 old_value, Atomic32 new_value) {
+  Atomic32 res = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+  MemoryBarrier();
+  return res;
+}
+
+inline Atomic32 Release_CompareAndSwap(volatile Atomic32 *ptr,
+                                       Atomic32 old_value, Atomic32 new_value) {
+  MemoryBarrier();
+  Atomic32 res = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+  return res;
+}
+
+inline void NoBarrier_Store(volatile Atomic32 *ptr, Atomic32 value) {
+  *ptr = value;
+}
+
+inline void MemoryBarrier() { __asm__ __volatile__("sync" : : : "memory"); }
+
+inline void Acquire_Store(volatile Atomic32 *ptr, Atomic32 value) {
+  *ptr = value;
+  MemoryBarrier();
+}
+
+inline void Release_Store(volatile Atomic32 *ptr, Atomic32 value) {
+  MemoryBarrier();
+  *ptr = value;
+}
+
+inline Atomic32 NoBarrier_Load(volatile const Atomic32 *ptr) { return *ptr; }
+
+inline Atomic32 Acquire_Load(volatile const Atomic32 *ptr) {
+  Atomic32 value = *ptr;
+  MemoryBarrier();
+  return value;
+}
+
+inline Atomic32 Release_Load(volatile const Atomic32 *ptr) {
+  MemoryBarrier();
+  return *ptr;
+}
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#undef ATOMICOPS_COMPILER_BARRIER
+
+#endif  // GOOGLE_PROTOBUF_ATOMICOPS_INTERNALS_PPC_GCC_H_
diff --git a/src/google/protobuf/stubs/platform_macros.h b/src/google/protobuf/stubs/platform_macros.h
index 22b3572..4ba4b34 100644
--- a/src/google/protobuf/stubs/platform_macros.h
+++ b/src/google/protobuf/stubs/platform_macros.h
@@ -73,6 +73,9 @@
 #elif defined(_POWER) || defined(__powerpc64__) || defined(__PPC64__)
 #define GOOGLE_PROTOBUF_ARCH_POWER 1
 #define GOOGLE_PROTOBUF_ARCH_64_BIT 1
+#elif defined(__PPC__)
+#define GOOGLE_PROTOBUF_ARCH_PPC 1
+#define GOOGLE_PROTOBUF_ARCH_32_BIT 1
 #elif defined(__GNUC__)
 # if (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) || (__GNUC__ > 4))
 // We fallback to the generic Clang/GCC >= 4.7 implementation in atomicops.h