Merge pull request #228 from cconroy/sanitize-enums

Sanitize Enum names from collisions with reserved words.
diff --git a/javanano/pom.xml b/javanano/pom.xml
index 53dd666..7a2be9d 100644
--- a/javanano/pom.xml
+++ b/javanano/pom.xml
@@ -162,4 +162,64 @@
       </plugin>
     </plugins>
   </build>
+  <profiles>
+    <profile>
+      <id>release</id>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-source-plugin</artifactId>
+            <version>2.2.1</version>
+            <executions>
+              <execution>
+                <id>attach-sources</id>
+                <goals>
+                  <goal>jar-no-fork</goal>
+                </goals>
+              </execution>
+            </executions>
+          </plugin>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-javadoc-plugin</artifactId>
+            <version>2.9.1</version>
+            <executions>
+              <execution>
+                <id>attach-javadocs</id>
+                <goals>
+                  <goal>jar</goal>
+                </goals>
+              </execution>
+            </executions>
+          </plugin>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-gpg-plugin</artifactId>
+            <version>1.5</version>
+            <executions>
+              <execution>
+                <id>sign-artifacts</id>
+                <phase>verify</phase>
+                <goals>
+                  <goal>sign</goal>
+                </goals>
+              </execution>
+            </executions>
+          </plugin>
+          <plugin>
+            <groupId>org.sonatype.plugins</groupId>
+            <artifactId>nexus-staging-maven-plugin</artifactId>
+            <version>1.6.3</version>
+            <extensions>true</extensions>
+            <configuration>
+               <serverId>sonatype-nexus-staging</serverId>
+               <nexusUrl>https://oss.sonatype.org/</nexusUrl>
+               <autoReleaseAfterClose>false</autoReleaseAfterClose>
+            </configuration>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
 </project>
diff --git a/python/google/protobuf/__init__.py b/python/google/protobuf/__init__.py
index ef8c9c5..1345bd5 100755
--- a/python/google/protobuf/__init__.py
+++ b/python/google/protobuf/__init__.py
@@ -32,4 +32,4 @@
 #
 # Copyright 2007 Google Inc. All Rights Reserved.
 
-__version__ = '3.0.0-alpha-3-pre'
+__version__ = '3.0.0a3.dev0'
diff --git a/python/setup.py b/python/setup.py
index b97fdae..c18818e 100755
--- a/python/setup.py
+++ b/python/setup.py
@@ -44,6 +44,18 @@
 else:
   protoc = find_executable("protoc")
 
+def GetVersion():
+  """Gets the version from google/protobuf/__init__.py
+
+  Do not import google.protobuf.__init__ directly, because an installed protobuf
+  library may be loaded instead.
+
+  """
+  with open(os.path.join('google', 'protobuf', '__init__.py')) as version_file:
+    exec(version_file.read())
+    return __version__
+
+
 def generate_proto(source):
   """Invokes the Protocol Compiler to generate a _pb2.py from the given
   .proto file.  Does nothing if the output already exists and is newer than
@@ -150,7 +162,7 @@
     os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'cpp'
 
   setup(name = 'protobuf',
-        version = '3.0.0-alpha-3-pre',
+        version = GetVersion(),
         packages = [ 'google' ],
         namespace_packages = [ 'google' ],
         google_test_dir = "google/protobuf/internal",
diff --git a/ruby/.gitignore b/ruby/.gitignore
new file mode 100644
index 0000000..80c978f
--- /dev/null
+++ b/ruby/.gitignore
@@ -0,0 +1,6 @@
+*.bundle
+tags
+.idea/
+lib/google/protobuf_java.jar
+protobuf-jruby.iml
+target/
diff --git a/ruby/Gemfile b/ruby/Gemfile
new file mode 100644
index 0000000..fa75df1
--- /dev/null
+++ b/ruby/Gemfile
@@ -0,0 +1,3 @@
+source 'https://rubygems.org'
+
+gemspec
diff --git a/ruby/Gemfile.lock b/ruby/Gemfile.lock
new file mode 100644
index 0000000..89deb47
--- /dev/null
+++ b/ruby/Gemfile.lock
@@ -0,0 +1,25 @@
+PATH
+  remote: .
+  specs:
+    google-protobuf (3.0.0.alpha.2)
+
+GEM
+  remote: https://rubygems.org/
+  specs:
+    power_assert (0.2.2)
+    rake (10.4.2)
+    rake-compiler (0.9.5)
+      rake
+    rubygems-tasks (0.2.4)
+    test-unit (3.0.9)
+      power_assert
+
+PLATFORMS
+  java
+  ruby
+
+DEPENDENCIES
+  google-protobuf!
+  rake-compiler
+  rubygems-tasks
+  test-unit
diff --git a/ruby/README.md b/ruby/README.md
index 8331d28..d2fa76a 100644
--- a/ruby/README.md
+++ b/ruby/README.md
@@ -60,15 +60,28 @@
 * Ruby development headers
 * a C compiler
 
-First, install the required Ruby gems:
+To Build the JRuby extension, you will need:
 
-    $ sudo gem install bundler rake rake-compiler rspec rubygems-tasks
+* Maven
+* The latest version of the protobuf java library
+* Install JRuby via rbenv or RVM
+
+First switch to the desired platform with rbenv or RVM.
+
+Then install the required Ruby gems:
+
+    $ gem install bundler
+    $ bundle
 
 Then build the Gem:
 
     $ rake gem
     $ gem install pkg/protobuf-$VERSION.gem
 
+To run the specs:
+
+    $ rake test
+
 This gem includes the upb parsing and serialization library as a single-file
 amalgamation. It is up-to-date with upb git commit
 `535bc2fe2f2b467f59347ffc9449e11e47791257`.
diff --git a/ruby/Rakefile b/ruby/Rakefile
index ae7d805..7c1d849 100644
--- a/ruby/Rakefile
+++ b/ruby/Rakefile
@@ -1,20 +1,32 @@
-require "rake/extensiontask"
+require "rubygems"
+require "rubygems/package_task"
+require "rake/extensiontask" unless RUBY_PLATFORM == "java"
 require "rake/testtask"
 
 spec = Gem::Specification.load("google-protobuf.gemspec")
 
-Rake::ExtensionTask.new("protobuf_c", spec) do |ext|
-  ext.ext_dir = "ext/google/protobuf_c"
-  ext.lib_dir = "lib/google"
+if RUBY_PLATFORM == "java"
+  task :clean do
+    system("mvn clean")
+  end
+
+  task :compile do
+    system("mvn package")
+  end
+else
+  Rake::ExtensionTask.new("protobuf_c", spec) do |ext|
+    ext.ext_dir = "ext/google/protobuf_c"
+    ext.lib_dir = "lib/google"
+  end
+end
+
+Gem::PackageTask.new(spec) do |pkg|
 end
 
 Rake::TestTask.new(:test => :build) do |t|
   t.test_files = FileList["tests/*.rb"]
 end
 
-Gem::PackageTask.new(spec) do |pkg|
-end
-
 task :build => [:clean, :compile]
 task :default => [:build]
 
diff --git a/ruby/google-protobuf.gemspec b/ruby/google-protobuf.gemspec
index 371009b..4c9449d 100644
--- a/ruby/google-protobuf.gemspec
+++ b/ruby/google-protobuf.gemspec
@@ -1,10 +1,3 @@
-class << Gem::Specification
-  def find_c_source(dir)
-    `cd #{dir}; git ls-files "*.c" "*.h" extconf.rb Makefile`.split
-    .map{|f| "#{dir}/#{f}"}
-  end
-end
-
 Gem::Specification.new do |s|
   s.name        = "google-protobuf"
   s.version     = "3.0.0.alpha.3.0.pre"
@@ -14,11 +7,17 @@
   s.authors     = ["Protobuf Authors"]
   s.email       = "protobuf@googlegroups.com"
   s.require_paths = ["lib"]
-  s.extensions  = ["ext/google/protobuf_c/extconf.rb"]
-  s.files       = ["lib/google/protobuf.rb"] +
-                  # extension C source
-                  find_c_source("ext/google/protobuf_c")
-  s.test_files = ["tests/basic.rb",
-		  "tests/stress.rb",
-		  "tests/generated_code_test.rb"]
+  s.files       = ["lib/google/protobuf.rb"]
+  unless RUBY_PLATFORM == "java"
+    s.files     += `git ls-files "*.c" "*.h" extconf.rb Makefile`.split
+    s.extensions= ["ext/google/protobuf_c/extconf.rb"]
+  else
+    s.files     += ["lib/google/protobuf_java.jar"]
+  end
+  s.test_files  = ["tests/basic.rb",
+                  "tests/stress.rb",
+                  "tests/generated_code_test.rb"]
+  s.add_development_dependency "rake-compiler"
+  s.add_development_dependency "test-unit"
+  s.add_development_dependency "rubygems-tasks"
 end
diff --git a/ruby/lib/google/protobuf.rb b/ruby/lib/google/protobuf.rb
index 7405333..75869dd 100644
--- a/ruby/lib/google/protobuf.rb
+++ b/ruby/lib/google/protobuf.rb
@@ -28,4 +28,9 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-require 'google/protobuf_c'
+if RUBY_PLATFORM == "java"
+  require 'json'
+  require 'google/protobuf_java'
+else
+  require 'google/protobuf_c'
+end
diff --git a/ruby/pom.xml b/ruby/pom.xml
new file mode 100644
index 0000000..1630fe8
--- /dev/null
+++ b/ruby/pom.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+      <groupId>com.google</groupId>
+      <artifactId>google</artifactId>
+      <version>1</version>
+    </parent>
+
+    <groupId>com.google.protobuf.jruby</groupId>
+    <artifactId>protobuf-jruby</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <name>Protocol Buffer JRuby native extension</name>
+    <description>
+      Protocol Buffers are a way of encoding structured data in an efficient yet
+      extensible format.
+    </description>
+    <inceptionYear>2014</inceptionYear>
+    <url>https://developers.google.com/protocol-buffers/</url>
+    <licenses>
+      <license>
+        <name>New BSD license</name>
+        <url>http://www.opensource.org/licenses/bsd-license.php</url>
+        <distribution>repo</distribution>
+      </license>
+    </licenses>
+    <scm>
+      <url>https://github.com/google/protobuf</url>
+      <connection>
+        scm:git:https://github.com/google/protobuf.git
+      </connection>
+    </scm>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <ruby.sources>lib/google</ruby.sources>
+        <jar.finalName>protobuf_java</jar.finalName>
+    </properties>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <configuration>
+                    <finalName>${jar.finalName}</finalName>
+                    <outputDirectory>${ruby.sources}</outputDirectory>
+                    <appendAssemblyId>false</appendAssemblyId>
+                    <descriptorRefs>
+                        <descriptorRef>jar-with-dependencies</descriptorRef>
+                    </descriptorRefs>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>make-assembly</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>single</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+    <dependencies>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-core</artifactId>
+            <version>2.4.3</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jruby</groupId>
+            <artifactId>jruby-complete</artifactId>
+            <version>1.7.13</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.google.protobuf</groupId>
+            <artifactId>protobuf-java</artifactId>
+            <version>3.0.0-pre</version>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyBuilder.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyBuilder.java
new file mode 100644
index 0000000..5addae5
--- /dev/null
+++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyBuilder.java
@@ -0,0 +1,167 @@
+/*
+ * Protocol Buffers - Google's data interchange format
+ * Copyright 2014 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.
+ */
+
+package com.google.protobuf.jruby;
+
+import org.jruby.*;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.*;
+import org.jruby.runtime.builtin.IRubyObject;
+
+@JRubyClass(name = "Builder")
+public class RubyBuilder extends RubyObject {
+    public static void createRubyBuilder(Ruby runtime) {
+        RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf");
+        RubyClass cBuilder = protobuf.defineClassUnder("Builder", runtime.getObject(), new ObjectAllocator() {
+            @Override
+            public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+                return new RubyBuilder(runtime, klazz);
+            }
+        });
+        cBuilder.defineAnnotatedMethods(RubyBuilder.class);
+    }
+
+    public RubyBuilder(Ruby runtime, RubyClass metaClass) {
+        super(runtime, metaClass);
+        this.cDescriptor = (RubyClass) runtime.getClassFromPath("Google::Protobuf::Descriptor");
+        this.cEnumDescriptor = (RubyClass) runtime.getClassFromPath("Google::Protobuf::EnumDescriptor");
+        this.cMessageBuilderContext = (RubyClass) runtime.getClassFromPath("Google::Protobuf::MessageBuilderContext");
+        this.cEnumBuilderContext = (RubyClass) runtime.getClassFromPath("Google::Protobuf::EnumBuilderContext");
+    }
+
+    /*
+     * call-seq:
+     *     Builder.new => builder
+     *
+     * Creates a new Builder. A Builder can accumulate a set of new message and enum
+     * descriptors and atomically register them into a pool in a way that allows for
+     * (co)recursive type references.
+     */
+    @JRubyMethod
+    public IRubyObject initialize(ThreadContext context) {
+        Ruby runtime = context.runtime;
+        this.pendingList = runtime.newArray();
+        return this;
+    }
+
+    /*
+     * call-seq:
+     *     Builder.add_message(name, &block)
+     *
+     * Creates a new, empty descriptor with the given name, and invokes the block in
+     * the context of a MessageBuilderContext on that descriptor. The block can then
+     * call, e.g., MessageBuilderContext#optional and MessageBuilderContext#repeated
+     * methods to define the message fields.
+     *
+     * This is the recommended, idiomatic way to build message definitions.
+     */
+    @JRubyMethod(name = "add_message")
+    public IRubyObject addMessage(ThreadContext context, IRubyObject name, Block block) {
+        RubyDescriptor msgdef = (RubyDescriptor) cDescriptor.newInstance(context, Block.NULL_BLOCK);
+        IRubyObject ctx = cMessageBuilderContext.newInstance(context, msgdef, this, Block.NULL_BLOCK);
+        msgdef.setName(context, name);
+        if (block.isGiven()) {
+            if (block.arity() == Arity.ONE_ARGUMENT) {
+                block.yield(context, ctx);
+            } else {
+                Binding binding = block.getBinding();
+                binding.setSelf(ctx);
+                block.yieldSpecific(context);
+            }
+        }
+        this.pendingList.add(msgdef);
+        return context.runtime.getNil();
+    }
+
+    /*
+     * call-seq:
+     *     Builder.add_enum(name, &block)
+     *
+     * Creates a new, empty enum descriptor with the given name, and invokes the block in
+     * the context of an EnumBuilderContext on that descriptor. The block can then
+     * call EnumBuilderContext#add_value to define the enum values.
+     *
+     * This is the recommended, idiomatic way to build enum definitions.
+     */
+    @JRubyMethod(name = "add_enum")
+    public IRubyObject addEnum(ThreadContext context, IRubyObject name, Block block) {
+        RubyEnumDescriptor enumDef = (RubyEnumDescriptor) cEnumDescriptor.newInstance(context, Block.NULL_BLOCK);
+        IRubyObject ctx = cEnumBuilderContext.newInstance(context, enumDef, Block.NULL_BLOCK);
+        enumDef.setName(context, name);
+
+        if (block.isGiven()) {
+            if (block.arity() == Arity.ONE_ARGUMENT) {
+                block.yield(context, ctx);
+            } else {
+                Binding binding = block.getBinding();
+                binding.setSelf(ctx);
+                block.yieldSpecific(context);
+            }
+        }
+
+        this.pendingList.add(enumDef);
+        return context.runtime.getNil();
+    }
+
+    /*
+     * call-seq:
+     *     Builder.finalize_to_pool(pool)
+     *
+     * Adds all accumulated message and enum descriptors created in this builder
+     * context to the given pool. The operation occurs atomically, and all
+     * descriptors can refer to each other (including in cycles). This is the only
+     * way to build (co)recursive message definitions.
+     *
+     * This method is usually called automatically by DescriptorPool#build after it
+     * invokes the given user block in the context of the builder. The user should
+     * not normally need to call this manually because a Builder is not normally
+     * created manually.
+     */
+    @JRubyMethod(name = "finalize_to_pool")
+    public IRubyObject finalizeToPool(ThreadContext context, IRubyObject rbPool) {
+        RubyDescriptorPool pool = (RubyDescriptorPool) rbPool;
+        for (int i = 0; i < this.pendingList.size(); i++) {
+            IRubyObject defRb = this.pendingList.entry(i);
+            if (defRb instanceof RubyDescriptor) {
+                pool.addToSymtab(context, (RubyDescriptor) defRb);
+            } else {
+                pool.addToSymtab(context, (RubyEnumDescriptor) defRb);
+            }
+        }
+        this.pendingList = context.runtime.newArray();
+        return context.runtime.getNil();
+    }
+
+    protected RubyArray pendingList;
+    private RubyClass cDescriptor, cEnumDescriptor, cMessageBuilderContext, cEnumBuilderContext;
+}
diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyDescriptor.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyDescriptor.java
new file mode 100644
index 0000000..51c50be
--- /dev/null
+++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyDescriptor.java
@@ -0,0 +1,267 @@
+/*
+ * Protocol Buffers - Google's data interchange format
+ * Copyright 2014 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.
+ */
+
+package com.google.protobuf.jruby;
+
+import com.google.protobuf.DescriptorProtos;
+import com.google.protobuf.Descriptors;
+import org.jruby.*;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.Block;
+import org.jruby.runtime.ObjectAllocator;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+@JRubyClass(name = "Descriptor", include = "Enumerable")
+public class RubyDescriptor extends RubyObject {
+    public static void createRubyDescriptor(Ruby runtime) {
+        RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf");
+        RubyClass cDescriptor = protobuf.defineClassUnder("Descriptor", runtime.getObject(), new ObjectAllocator() {
+            @Override
+            public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+                return new RubyDescriptor(runtime, klazz);
+            }
+        });
+        cDescriptor.includeModule(runtime.getEnumerable());
+        cDescriptor.defineAnnotatedMethods(RubyDescriptor.class);
+    }
+
+    public RubyDescriptor(Ruby runtime, RubyClass klazz) {
+        super(runtime, klazz);
+    }
+
+    /*
+     * call-seq:
+     *     Descriptor.new => descriptor
+     *
+     * Creates a new, empty, message type descriptor. At a minimum, its name must be
+     * set before it is added to a pool. It cannot be used to create messages until
+     * it is added to a pool, after which it becomes immutable (as part of a
+     * finalization process).
+     */
+    @JRubyMethod
+    public IRubyObject initialize(ThreadContext context) {
+        this.builder = DescriptorProtos.DescriptorProto.newBuilder();
+        this.fieldDefMap = new HashMap<String, RubyFieldDescriptor>();
+        this.oneofDefs = new HashMap<IRubyObject, RubyOneofDescriptor>();
+        return this;
+    }
+
+    /*
+     * call-seq:
+     *     Descriptor.name => name
+     *
+     * Returns the name of this message type as a fully-qualfied string (e.g.,
+     * My.Package.MessageType).
+     */
+    @JRubyMethod(name = "name")
+    public IRubyObject getName(ThreadContext context) {
+        return this.name;
+    }
+
+    /*
+     * call-seq:
+     *    Descriptor.name = name
+     *
+     * Assigns a name to this message type. The descriptor must not have been added
+     * to a pool yet.
+     */
+    @JRubyMethod(name = "name=")
+    public IRubyObject setName(ThreadContext context, IRubyObject name) {
+        this.name = name;
+        this.builder.setName(Utils.escapeIdentifier(this.name.asJavaString()));
+        return context.runtime.getNil();
+    }
+
+    /*
+     * call-seq:
+     *     Descriptor.add_field(field) => nil
+     *
+     * Adds the given FieldDescriptor to this message type. The descriptor must not
+     * have been added to a pool yet. Raises an exception if a field with the same
+     * name or number already exists. Sub-type references (e.g. for fields of type
+     * message) are not resolved at this point.
+     */
+    @JRubyMethod(name = "add_field")
+    public IRubyObject addField(ThreadContext context, IRubyObject obj) {
+        RubyFieldDescriptor fieldDef = (RubyFieldDescriptor) obj;
+        this.fieldDefMap.put(fieldDef.getName(context).asJavaString(), fieldDef);
+        this.builder.addField(fieldDef.build());
+        return context.runtime.getNil();
+    }
+
+    /*
+     * call-seq:
+     *     Descriptor.lookup(name) => FieldDescriptor
+     *
+     * Returns the field descriptor for the field with the given name, if present,
+     * or nil if none.
+     */
+    @JRubyMethod
+    public IRubyObject lookup(ThreadContext context, IRubyObject fieldName) {
+        return this.fieldDefMap.get(fieldName.asJavaString());
+    }
+
+    /*
+     * call-seq:
+     *     Descriptor.msgclass => message_klass
+     *
+     * Returns the Ruby class created for this message type. Valid only once the
+     * message type has been added to a pool.
+     */
+    @JRubyMethod
+    public IRubyObject msgclass(ThreadContext context) {
+        if (this.klazz == null) {
+            this.klazz = buildClassFromDescriptor(context);
+        }
+        return this.klazz;
+    }
+
+    /*
+     * call-seq:
+     *     Descriptor.each(&block)
+     *
+     * Iterates over fields in this message type, yielding to the block on each one.
+     */
+    @JRubyMethod
+    public IRubyObject each(ThreadContext context, Block block) {
+        for (Map.Entry<String, RubyFieldDescriptor> entry : fieldDefMap.entrySet()) {
+            block.yield(context, entry.getValue());
+        }
+        return context.runtime.getNil();
+    }
+
+    /*
+     * call-seq:
+     *     Descriptor.add_oneof(oneof) => nil
+     *
+     * Adds the given OneofDescriptor to this message type. This descriptor must not
+     * have been added to a pool yet. Raises an exception if a oneof with the same
+     * name already exists, or if any of the oneof's fields' names or numbers
+     * conflict with an existing field in this message type. All fields in the oneof
+     * are added to the message descriptor. Sub-type references (e.g. for fields of
+     * type message) are not resolved at this point.
+     */
+    @JRubyMethod(name = "add_oneof")
+    public IRubyObject addOneof(ThreadContext context, IRubyObject obj) {
+        RubyOneofDescriptor def = (RubyOneofDescriptor) obj;
+        builder.addOneofDecl(def.build(builder.getOneofDeclCount()));
+        for (RubyFieldDescriptor fieldDescriptor : def.getFields()) {
+            addField(context, fieldDescriptor);
+        }
+        oneofDefs.put(def.getName(context), def);
+        return context.runtime.getNil();
+    }
+
+    /*
+     * call-seq:
+     *     Descriptor.each_oneof(&block) => nil
+     *
+     * Invokes the given block for each oneof in this message type, passing the
+     * corresponding OneofDescriptor.
+     */
+    @JRubyMethod(name = "each_oneof")
+    public IRubyObject eachOneof(ThreadContext context, Block block) {
+        for (RubyOneofDescriptor oneofDescriptor : oneofDefs.values()) {
+            block.yieldSpecific(context, oneofDescriptor);
+        }
+        return context.runtime.getNil();
+    }
+
+    /*
+     * call-seq:
+     *     Descriptor.lookup_oneof(name) => OneofDescriptor
+     *
+     * Returns the oneof descriptor for the oneof with the given name, if present,
+     * or nil if none.
+     */
+    @JRubyMethod(name = "lookup_oneof")
+    public IRubyObject lookupOneof(ThreadContext context, IRubyObject name) {
+        if (name instanceof RubySymbol) {
+            name = ((RubySymbol) name).id2name();
+        }
+        return oneofDefs.containsKey(name) ? oneofDefs.get(name) : context.runtime.getNil();
+    }
+
+    public void setDescriptor(Descriptors.Descriptor descriptor) {
+        this.descriptor = descriptor;
+    }
+
+    public Descriptors.Descriptor getDescriptor() {
+        return this.descriptor;
+    }
+
+    public DescriptorProtos.DescriptorProto.Builder getBuilder() {
+        return builder;
+    }
+
+    public void setMapEntry(boolean isMapEntry) {
+        this.builder.setOptions(DescriptorProtos.MessageOptions.newBuilder().setMapEntry(isMapEntry));
+    }
+
+    private RubyModule buildClassFromDescriptor(ThreadContext context) {
+        Ruby runtime = context.runtime;
+
+        ObjectAllocator allocator = new ObjectAllocator() {
+            @Override
+            public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+                return new RubyMessage(runtime, klazz, descriptor);
+            }
+        };
+
+        // rb_define_class_id
+        RubyClass klass = RubyClass.newClass(runtime, runtime.getObject());
+        klass.setAllocator(allocator);
+        klass.makeMetaClass(runtime.getObject().getMetaClass());
+        klass.inherit(runtime.getObject());
+        klass.instance_variable_set(runtime.newString(Utils.DESCRIPTOR_INSTANCE_VAR), this);
+        klass.defineAnnotatedMethods(RubyMessage.class);
+        return klass;
+    }
+
+    protected RubyFieldDescriptor lookup(String fieldName) {
+        return fieldDefMap.get(Utils.unescapeIdentifier(fieldName));
+    }
+
+    private IRubyObject name;
+    private RubyModule klazz;
+
+    private DescriptorProtos.DescriptorProto.Builder builder;
+    private Descriptors.Descriptor descriptor;
+    private Map<String, RubyFieldDescriptor> fieldDefMap;
+    private Map<IRubyObject, RubyOneofDescriptor> oneofDefs;
+}
diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyDescriptorPool.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyDescriptorPool.java
new file mode 100644
index 0000000..0345cb9
--- /dev/null
+++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyDescriptorPool.java
@@ -0,0 +1,169 @@
+/*
+ * Protocol Buffers - Google's data interchange format
+ * Copyright 2014 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.
+ */
+
+package com.google.protobuf.jruby;
+
+import com.google.protobuf.DescriptorProtos;
+import com.google.protobuf.Descriptors;
+import org.jruby.*;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.*;
+import org.jruby.runtime.builtin.IRubyObject;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@JRubyClass(name = "DescriptorPool")
+public class RubyDescriptorPool extends RubyObject {
+    public static void createRubyDescriptorPool(Ruby runtime) {
+        RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf");
+        RubyClass cDescriptorPool = protobuf.defineClassUnder("DescriptorPool", runtime.getObject(), new ObjectAllocator() {
+            @Override
+            public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+                return new RubyDescriptorPool(runtime, klazz);
+            }
+        });
+
+        cDescriptorPool.defineAnnotatedMethods(RubyDescriptorPool.class);
+        descriptorPool = (RubyDescriptorPool) cDescriptorPool.newInstance(runtime.getCurrentContext(), Block.NULL_BLOCK);
+    }
+
+    public RubyDescriptorPool(Ruby ruby, RubyClass klazz) {
+        super(ruby, klazz);
+    }
+
+    @JRubyMethod
+    public IRubyObject initialize(ThreadContext context) {
+        this.symtab = new HashMap<IRubyObject, IRubyObject>();
+        this.cBuilder = (RubyClass) context.runtime.getClassFromPath("Google::Protobuf::Builder");
+        this.builder = DescriptorProtos.FileDescriptorProto.newBuilder();
+        return this;
+    }
+
+    @JRubyMethod
+    public IRubyObject build(ThreadContext context, Block block) {
+        RubyBuilder ctx = (RubyBuilder) cBuilder.newInstance(context, Block.NULL_BLOCK);
+        if (block.arity() == Arity.ONE_ARGUMENT) {
+            block.yield(context, ctx);
+        } else {
+            Binding binding = block.getBinding();
+            binding.setSelf(ctx);
+            block.yieldSpecific(context);
+        }
+        ctx.finalizeToPool(context, this);
+        buildFileDescriptor(context);
+        return context.runtime.getNil();
+    }
+
+    @JRubyMethod
+    public IRubyObject lookup(ThreadContext context, IRubyObject name) {
+        IRubyObject descriptor = this.symtab.get(name);
+        if (descriptor == null) {
+            return context.runtime.getNil();
+        }
+        return descriptor;
+    }
+
+    /*
+     * call-seq:
+     *     DescriptorPool.generated_pool => descriptor_pool
+     *
+     * Class method that returns the global DescriptorPool. This is a singleton into
+     * which generated-code message and enum types are registered. The user may also
+     * register types in this pool for convenience so that they do not have to hold
+     * a reference to a private pool instance.
+     */
+    @JRubyMethod(meta = true, name = "generated_pool")
+    public static IRubyObject generatedPool(ThreadContext context, IRubyObject recv) {
+        return descriptorPool;
+    }
+
+    protected void addToSymtab(ThreadContext context, RubyDescriptor def) {
+        symtab.put(def.getName(context), def);
+        this.builder.addMessageType(def.getBuilder());
+    }
+
+    protected void addToSymtab(ThreadContext context, RubyEnumDescriptor def) {
+        symtab.put(def.getName(context), def);
+        this.builder.addEnumType(def.getBuilder());
+    }
+
+    private void buildFileDescriptor(ThreadContext context) {
+        Ruby runtime = context.runtime;
+        try {
+            this.builder.setSyntax("proto3");
+            final Descriptors.FileDescriptor fileDescriptor = Descriptors.FileDescriptor.buildFrom(
+                    this.builder.build(), new Descriptors.FileDescriptor[]{});
+
+            for (Descriptors.EnumDescriptor enumDescriptor : fileDescriptor.getEnumTypes()) {
+                String enumName = Utils.unescapeIdentifier(enumDescriptor.getName());
+                if (enumDescriptor.findValueByNumber(0) == null) {
+                    throw runtime.newTypeError("Enum definition " + enumName
+                            + " does not contain a value for '0'");
+                }
+                ((RubyEnumDescriptor) symtab.get(runtime.newString(enumName)))
+                        .setDescriptor(enumDescriptor);
+            }
+            for (Descriptors.Descriptor descriptor : fileDescriptor.getMessageTypes()) {
+                RubyDescriptor rubyDescriptor = ((RubyDescriptor)
+                        symtab.get(runtime.newString(Utils.unescapeIdentifier(descriptor.getName()))));
+                for (Descriptors.FieldDescriptor fieldDescriptor : descriptor.getFields()) {
+                    if (fieldDescriptor.isRequired()) {
+                        throw runtime.newTypeError("Required fields are unsupported in proto3");
+                    }
+                    RubyFieldDescriptor rubyFieldDescriptor = rubyDescriptor.lookup(fieldDescriptor.getName());
+                    rubyFieldDescriptor.setFieldDef(fieldDescriptor);
+                    if (fieldDescriptor.getType() == Descriptors.FieldDescriptor.Type.MESSAGE) {
+                        RubyDescriptor subType = (RubyDescriptor) lookup(context,
+                                runtime.newString(Utils.unescapeIdentifier(fieldDescriptor.getMessageType().getName())));
+                        rubyFieldDescriptor.setSubType(subType);
+                    }
+                    if (fieldDescriptor.getType() == Descriptors.FieldDescriptor.Type.ENUM) {
+                        RubyEnumDescriptor subType = (RubyEnumDescriptor) lookup(context,
+                                runtime.newString(Utils.unescapeIdentifier(fieldDescriptor.getEnumType().getName())));
+                        rubyFieldDescriptor.setSubType(subType);
+                    }
+                }
+                rubyDescriptor.setDescriptor(descriptor);
+            }
+        } catch (Descriptors.DescriptorValidationException e) {
+            throw runtime.newRuntimeError(e.getMessage());
+        }
+    }
+
+    private static RubyDescriptorPool descriptorPool;
+
+    private RubyClass cBuilder;
+    private Map<IRubyObject, IRubyObject> symtab;
+    private DescriptorProtos.FileDescriptorProto.Builder builder;
+}
diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyEnum.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyEnum.java
new file mode 100644
index 0000000..929d869
--- /dev/null
+++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyEnum.java
@@ -0,0 +1,86 @@
+/*
+ * Protocol Buffers - Google's data interchange format
+ * Copyright 2014 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.
+ */
+
+package com.google.protobuf.jruby;
+
+import com.google.protobuf.Descriptors;
+import org.jruby.RubyModule;
+import org.jruby.RubyNumeric;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+
+public class RubyEnum {
+    /*
+     * call-seq:
+     *     Enum.lookup(number) => name
+     *
+     * This module method, provided on each generated enum module, looks up an enum
+     * value by number and returns its name as a Ruby symbol, or nil if not found.
+     */
+    @JRubyMethod(meta = true)
+    public static IRubyObject lookup(ThreadContext context, IRubyObject recv, IRubyObject number) {
+        RubyEnumDescriptor rubyEnumDescriptorescriptor = (RubyEnumDescriptor) getDescriptor(context, recv);
+        Descriptors.EnumDescriptor descriptor = rubyEnumDescriptorescriptor.getDescriptor();
+        Descriptors.EnumValueDescriptor value = descriptor.findValueByNumber(RubyNumeric.num2int(number));
+        if (value == null) return context.runtime.getNil();
+        return context.runtime.newSymbol(value.getName());
+    }
+
+    /*
+     * call-seq:
+     *     Enum.resolve(name) => number
+     *
+     * This module method, provided on each generated enum module, looks up an enum
+     * value by name (as a Ruby symbol) and returns its name, or nil if not found.
+     */
+    @JRubyMethod(meta = true)
+    public static IRubyObject resolve(ThreadContext context, IRubyObject recv, IRubyObject name) {
+        RubyEnumDescriptor rubyEnumDescriptorescriptor = (RubyEnumDescriptor) getDescriptor(context, recv);
+        Descriptors.EnumDescriptor descriptor = rubyEnumDescriptorescriptor.getDescriptor();
+        Descriptors.EnumValueDescriptor value = descriptor.findValueByName(name.asJavaString());
+        if (value == null) return context.runtime.getNil();
+        return context.runtime.newFixnum(value.getNumber());
+    }
+
+    /*
+     * call-seq:
+     *     Enum.descriptor
+     *
+     * This module method, provided on each generated enum module, returns the
+     * EnumDescriptor corresponding to this enum type.
+     */
+    @JRubyMethod(meta = true, name = "descriptor")
+    public static IRubyObject getDescriptor(ThreadContext context, IRubyObject recv) {
+        return ((RubyModule) recv).getInstanceVariable(Utils.DESCRIPTOR_INSTANCE_VAR);
+    }
+}
diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyEnumBuilderContext.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyEnumBuilderContext.java
new file mode 100644
index 0000000..e4cac34
--- /dev/null
+++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyEnumBuilderContext.java
@@ -0,0 +1,82 @@
+/*
+ * Protocol Buffers - Google's data interchange format
+ * Copyright 2014 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.
+ */
+
+package com.google.protobuf.jruby;
+
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.RubyModule;
+import org.jruby.RubyObject;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.ObjectAllocator;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+
+@JRubyClass(name = "EnumBuilderContext")
+public class RubyEnumBuilderContext extends RubyObject {
+    public static void createRubyEnumBuilderContext(Ruby runtime) {
+        RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf");
+        RubyClass cMessageBuilderContext = protobuf.defineClassUnder("EnumBuilderContext", runtime.getObject(), new ObjectAllocator() {
+            @Override
+            public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+                return new RubyEnumBuilderContext(runtime, klazz);
+            }
+        });
+        cMessageBuilderContext.defineAnnotatedMethods(RubyEnumBuilderContext.class);
+    }
+
+    public RubyEnumBuilderContext(Ruby ruby, RubyClass klazz) {
+        super(ruby, klazz);
+    }
+
+    @JRubyMethod
+    public IRubyObject initialize(ThreadContext context, IRubyObject enumDescriptor) {
+        this.enumDescriptor = (RubyEnumDescriptor) enumDescriptor;
+        return this;
+    }
+
+    /*
+     * call-seq:
+     *     EnumBuilder.add_value(name, number)
+     *
+     * Adds the given name => number mapping to the enum type. Name must be a Ruby
+     * symbol.
+     */
+    @JRubyMethod
+    public IRubyObject value(ThreadContext context, IRubyObject name, IRubyObject number) {
+        this.enumDescriptor.addValue(context, name, number);
+        return context.runtime.getNil();
+    }
+
+    private RubyEnumDescriptor enumDescriptor;
+}
diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyEnumDescriptor.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyEnumDescriptor.java
new file mode 100644
index 0000000..4df832d
--- /dev/null
+++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyEnumDescriptor.java
@@ -0,0 +1,185 @@
+/*
+ * Protocol Buffers - Google's data interchange format
+ * Copyright 2014 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.
+ */
+
+package com.google.protobuf.jruby;
+
+import com.google.protobuf.DescriptorProtos;
+import com.google.protobuf.Descriptors;
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.RubyModule;
+import org.jruby.RubyObject;
+import org.jruby.RubyNumeric;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.Block;
+import org.jruby.runtime.ObjectAllocator;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+
+@JRubyClass(name = "EnumDescriptor", include = "Enumerable")
+public class RubyEnumDescriptor extends RubyObject {
+    public static void createRubyEnumDescriptor(Ruby runtime) {
+        RubyModule mProtobuf = runtime.getClassFromPath("Google::Protobuf");
+        RubyClass cEnumDescriptor = mProtobuf.defineClassUnder("EnumDescriptor", runtime.getObject(), new ObjectAllocator() {
+            @Override
+            public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+                return new RubyEnumDescriptor(runtime, klazz);
+            }
+        });
+        cEnumDescriptor.includeModule(runtime.getEnumerable());
+        cEnumDescriptor.defineAnnotatedMethods(RubyEnumDescriptor.class);
+    }
+
+    public RubyEnumDescriptor(Ruby runtime, RubyClass klazz) {
+        super(runtime, klazz);
+    }
+
+    /*
+     * call-seq:
+     *     EnumDescriptor.new => enum_descriptor
+     *
+     * Creates a new, empty, enum descriptor. Must be added to a pool before the
+     * enum type can be used. The enum type may only be modified prior to adding to
+     * a pool.
+     */
+    @JRubyMethod
+    public IRubyObject initialize(ThreadContext context) {
+        this.builder = DescriptorProtos.EnumDescriptorProto.newBuilder();
+        return this;
+    }
+
+    /*
+     * call-seq:
+     *     EnumDescriptor.name => name
+     *
+     * Returns the name of this enum type.
+     */
+    @JRubyMethod(name = "name")
+    public IRubyObject getName(ThreadContext context) {
+        return this.name;
+    }
+
+    /*
+     * call-seq:
+     *     EnumDescriptor.name = name
+     *
+     * Sets the name of this enum type. Cannot be called if the enum type has
+     * already been added to a pool.
+     */
+    @JRubyMethod(name = "name=")
+    public IRubyObject setName(ThreadContext context, IRubyObject name) {
+        this.name = name;
+        this.builder.setName(Utils.escapeIdentifier(name.asJavaString()));
+        return context.runtime.getNil();
+    }
+
+    /*
+     * call-seq:
+     *     EnumDescriptor.add_value(key, value)
+     *
+     * Adds a new key => value mapping to this enum type. Key must be given as a
+     * Ruby symbol. Cannot be called if the enum type has already been added to a
+     * pool. Will raise an exception if the key or value is already in use.
+     */
+    @JRubyMethod(name = "add_value")
+    public IRubyObject addValue(ThreadContext context, IRubyObject name, IRubyObject number) {
+        DescriptorProtos.EnumValueDescriptorProto.Builder valueBuilder = DescriptorProtos.EnumValueDescriptorProto.newBuilder();
+        valueBuilder.setName(name.asJavaString());
+        valueBuilder.setNumber(RubyNumeric.num2int(number));
+        this.builder.addValue(valueBuilder);
+        return context.runtime.getNil();
+    }
+
+    /*
+     * call-seq:
+     *     EnumDescriptor.each(&block)
+     *
+     * Iterates over key => value mappings in this enum's definition, yielding to
+     * the block with (key, value) arguments for each one.
+     */
+    @JRubyMethod
+    public IRubyObject each(ThreadContext context, Block block) {
+        Ruby runtime = context.runtime;
+        for (Descriptors.EnumValueDescriptor enumValueDescriptor : descriptor.getValues()) {
+            block.yield(context, runtime.newArray(runtime.newSymbol(enumValueDescriptor.getName()),
+                    runtime.newFixnum(enumValueDescriptor.getNumber())));
+        }
+        return runtime.getNil();
+    }
+
+    /*
+     * call-seq:
+     *     EnumDescriptor.enummodule => module
+     *
+     * Returns the Ruby module corresponding to this enum type. Cannot be called
+     * until the enum descriptor has been added to a pool.
+     */
+    @JRubyMethod
+    public IRubyObject enummodule(ThreadContext context) {
+        if (this.klazz == null) {
+            this.klazz = buildModuleFromDescriptor(context);
+        }
+        return this.klazz;
+    }
+
+    public void setDescriptor(Descriptors.EnumDescriptor descriptor) {
+        this.descriptor = descriptor;
+    }
+
+    public Descriptors.EnumDescriptor getDescriptor() {
+        return this.descriptor;
+    }
+
+    public DescriptorProtos.EnumDescriptorProto.Builder getBuilder() {
+        return this.builder;
+    }
+
+    private RubyModule buildModuleFromDescriptor(ThreadContext context) {
+        Ruby runtime = context.runtime;
+        Utils.checkNameAvailability(context, name.asJavaString());
+
+        RubyModule enumModule = RubyModule.newModule(runtime);
+        for (Descriptors.EnumValueDescriptor value : descriptor.getValues()) {
+            enumModule.defineConstant(value.getName(), runtime.newFixnum(value.getNumber()));
+        }
+
+        enumModule.instance_variable_set(runtime.newString(Utils.DESCRIPTOR_INSTANCE_VAR), this);
+        enumModule.defineAnnotatedMethods(RubyEnum.class);
+        return enumModule;
+    }
+
+    private IRubyObject name;
+    private RubyModule klazz;
+    private Descriptors.EnumDescriptor descriptor;
+    private DescriptorProtos.EnumDescriptorProto.Builder builder;
+}
diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyFieldDescriptor.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyFieldDescriptor.java
new file mode 100644
index 0000000..38226c4
--- /dev/null
+++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyFieldDescriptor.java
@@ -0,0 +1,248 @@
+/*
+ * Protocol Buffers - Google's data interchange format
+ * Copyright 2014 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.
+ */
+
+package com.google.protobuf.jruby;
+
+import com.google.protobuf.DescriptorProtos;
+import com.google.protobuf.Descriptors;
+import org.jruby.*;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.ObjectAllocator;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+
+@JRubyClass(name = "FieldDescriptor")
+public class RubyFieldDescriptor extends RubyObject {
+    public static void createRubyFileDescriptor(Ruby runtime) {
+        RubyModule mProtobuf = runtime.getClassFromPath("Google::Protobuf");
+        RubyClass cFieldDescriptor = mProtobuf.defineClassUnder("FieldDescriptor", runtime.getObject(), new ObjectAllocator() {
+            @Override
+            public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+                return new RubyFieldDescriptor(runtime, klazz);
+            }
+        });
+        cFieldDescriptor.defineAnnotatedMethods(RubyFieldDescriptor.class);
+    }
+
+    public RubyFieldDescriptor(Ruby runtime, RubyClass klazz) {
+        super(runtime, klazz);
+    }
+
+    /*
+     * call-seq:
+     *     FieldDescriptor.new => field
+     *
+     * Returns a new field descriptor. Its name, type, etc. must be set before it is
+     * added to a message type.
+     */
+    @JRubyMethod
+    public IRubyObject initialize(ThreadContext context) {
+        builder = DescriptorProtos.FieldDescriptorProto.newBuilder();
+        return this;
+    }
+
+    /*
+     * call-seq:
+     *     FieldDescriptor.label = label
+     *
+     * Sets the label on this field. Cannot be called if field is part of a message
+     * type already in a pool.
+     */
+    @JRubyMethod(name = "label=")
+    public IRubyObject setLabel(ThreadContext context, IRubyObject value) {
+        this.builder.setLabel(
+                DescriptorProtos.FieldDescriptorProto.Label.valueOf("LABEL_" + value.asJavaString().toUpperCase()));
+        return context.runtime.getNil();
+    }
+
+    /*
+     * call-seq:
+     *     FieldDescriptor.name => name
+     *
+     * Returns the name of this field.
+     */
+    @JRubyMethod(name = "name")
+    public IRubyObject getName(ThreadContext context) {
+        return this.name;
+    }
+
+    @JRubyMethod(name = "subtype")
+    public IRubyObject getSubType(ThreadContext context) {
+        return subType;
+    }
+
+    /*
+     * call-seq:
+     *     FieldDescriptor.name = name
+     *
+     * Sets the name of this field. Cannot be called once the containing message
+     * type, if any, is added to a pool.
+     */
+    @JRubyMethod(name = "name=")
+    public IRubyObject setName(ThreadContext context, IRubyObject value) {
+        String nameStr = value.asJavaString();
+        this.name = context.runtime.newString(nameStr);
+        this.builder.setName(Utils.escapeIdentifier(nameStr));
+        return context.runtime.getNil();
+    }
+
+    /*
+     * call-seq:
+     *     FieldDescriptor.type => type
+     *
+     * Returns this field's type, as a Ruby symbol, or nil if not yet set.
+     *
+     * Valid field types are:
+     *     :int32, :int64, :uint32, :uint64, :float, :double, :bool, :string,
+     *     :bytes, :message.
+     */
+    @JRubyMethod(name = "type")
+    public IRubyObject getType(ThreadContext context) {
+        return Utils.fieldTypeToRuby(context, this.builder.getType());
+    }
+
+    /*
+     * call-seq:
+     *     FieldDescriptor.type = type
+     *
+     * Sets this field's type. Cannot be called if field is part of a message type
+     * already in a pool.
+     */
+    @JRubyMethod(name = "type=")
+    public IRubyObject setType(ThreadContext context, IRubyObject value) {
+        this.builder.setType(DescriptorProtos.FieldDescriptorProto.Type.valueOf("TYPE_" + value.asJavaString().toUpperCase()));
+        return context.runtime.getNil();
+    }
+
+    /*
+     * call-seq:
+     *     FieldDescriptor.number = number
+     *
+     * Sets the tag number for this field. Cannot be called if field is part of a
+     * message type already in a pool.
+     */
+    @JRubyMethod(name = "number=")
+    public IRubyObject setNumber(ThreadContext context, IRubyObject value) {
+        this.builder.setNumber(RubyNumeric.num2int(value));
+        return context.runtime.getNil();
+    }
+
+    /*
+     * call-seq:
+     *     FieldDescriptor.submsg_name = submsg_name
+     *
+     * Sets the name of the message or enum type corresponding to this field, if it
+     * is a message or enum field (respectively). This type name will be resolved
+     * within the context of the pool to which the containing message type is added.
+     * Cannot be called on field that are not of message or enum type, or on fields
+     * that are part of a message type already added to a pool.
+     */
+    @JRubyMethod(name = "submsg_name=")
+    public IRubyObject setSubmsgName(ThreadContext context, IRubyObject name) {
+        this.builder.setTypeName("." + Utils.escapeIdentifier(name.asJavaString()));
+        return context.runtime.getNil();
+    }
+
+    /*
+     * call-seq:
+     *     FieldDescriptor.get(message) => value
+     *
+     * Returns the value set for this field on the given message. Raises an
+     * exception if message is of the wrong type.
+     */
+    @JRubyMethod(name = "get")
+    public IRubyObject getValue(ThreadContext context, IRubyObject msgRb) {
+        RubyMessage message = (RubyMessage) msgRb;
+        if (message.getDescriptor() != fieldDef.getContainingType()) {
+            throw context.runtime.newTypeError("set method called on wrong message type");
+        }
+        return message.getField(context, fieldDef);
+    }
+
+    /*
+     * call-seq:
+     *     FieldDescriptor.set(message, value)
+     *
+     * Sets the value corresponding to this field to the given value on the given
+     * message. Raises an exception if message is of the wrong type. Performs the
+     * ordinary type-checks for field setting.
+     */
+    @JRubyMethod(name = "set")
+    public IRubyObject setValue(ThreadContext context, IRubyObject msgRb, IRubyObject value) {
+        RubyMessage message = (RubyMessage) msgRb;
+        if (message.getDescriptor() != fieldDef.getContainingType()) {
+            throw context.runtime.newTypeError("set method called on wrong message type");
+        }
+        message.setField(context, fieldDef, value);
+        return context.runtime.getNil();
+    }
+
+    protected void setSubType(IRubyObject rubyDescriptor) {
+        this.subType = rubyDescriptor;
+    }
+
+    protected void setFieldDef(Descriptors.FieldDescriptor fieldDescriptor) {
+        this.fieldDef = fieldDescriptor;
+    }
+
+    protected void setOneofName(IRubyObject name) {
+        oneofName = name;
+    }
+
+    protected void setOneofIndex(int index) {
+        hasOneofIndex = true;
+        oneofIndex = index;
+    }
+
+    protected IRubyObject getOneofName() {
+        return oneofName;
+    }
+
+    protected Descriptors.FieldDescriptor getFieldDef() {
+        return fieldDef;
+    }
+
+    protected DescriptorProtos.FieldDescriptorProto build() {
+        if (hasOneofIndex)
+            builder.setOneofIndex(oneofIndex);
+        return this.builder.build();
+    }
+
+    private DescriptorProtos.FieldDescriptorProto.Builder builder;
+    private IRubyObject name;
+    private IRubyObject subType;
+    private IRubyObject oneofName;
+    private Descriptors.FieldDescriptor fieldDef;
+    private int oneofIndex;
+    private boolean hasOneofIndex = false;
+}
diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyMap.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyMap.java
new file mode 100644
index 0000000..b25dc6e
--- /dev/null
+++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyMap.java
@@ -0,0 +1,434 @@
+/*
+ * Protocol Buffers - Google's data interchange format
+ * Copyright 2014 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.
+ */
+
+package com.google.protobuf.jruby;
+
+import com.google.protobuf.Descriptors;
+import com.google.protobuf.DynamicMessage;
+import com.google.protobuf.MapEntry;
+import org.jruby.*;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.internal.runtime.methods.DynamicMethod;
+import org.jruby.runtime.Block;
+import org.jruby.runtime.ObjectAllocator;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.jruby.util.ByteList;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@JRubyClass(name = "Map", include = "Enumerable")
+public class RubyMap extends RubyObject {
+    public static void createRubyMap(Ruby runtime) {
+        RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf");
+        RubyClass cMap = protobuf.defineClassUnder("Map", runtime.getObject(), new ObjectAllocator() {
+            @Override
+            public IRubyObject allocate(Ruby ruby, RubyClass rubyClass) {
+                return new RubyMap(ruby, rubyClass);
+            }
+        });
+        cMap.includeModule(runtime.getEnumerable());
+        cMap.defineAnnotatedMethods(RubyMap.class);
+    }
+
+    public RubyMap(Ruby ruby, RubyClass rubyClass) {
+        super(ruby, rubyClass);
+    }
+
+    /*
+     * call-seq:
+     *     Map.new(key_type, value_type, value_typeclass = nil, init_hashmap = {})
+     *     => new map
+     *
+     * Allocates a new Map container. This constructor may be called with 2, 3, or 4
+     * arguments. The first two arguments are always present and are symbols (taking
+     * on the same values as field-type symbols in message descriptors) that
+     * indicate the type of the map key and value fields.
+     *
+     * The supported key types are: :int32, :int64, :uint32, :uint64, :bool,
+     * :string, :bytes.
+     *
+     * The supported value types are: :int32, :int64, :uint32, :uint64, :bool,
+     * :string, :bytes, :enum, :message.
+     *
+     * The third argument, value_typeclass, must be present if value_type is :enum
+     * or :message. As in RepeatedField#new, this argument must be a message class
+     * (for :message) or enum module (for :enum).
+     *
+     * The last argument, if present, provides initial content for map. Note that
+     * this may be an ordinary Ruby hashmap or another Map instance with identical
+     * key and value types. Also note that this argument may be present whether or
+     * not value_typeclass is present (and it is unambiguously separate from
+     * value_typeclass because value_typeclass's presence is strictly determined by
+     * value_type). The contents of this initial hashmap or Map instance are
+     * shallow-copied into the new Map: the original map is unmodified, but
+     * references to underlying objects will be shared if the value type is a
+     * message type.
+     */
+
+    @JRubyMethod(required = 2, optional = 2)
+    public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
+        this.table = new HashMap<IRubyObject, IRubyObject>();
+        this.keyType = Utils.rubyToFieldType(args[0]);
+        this.valueType = Utils.rubyToFieldType(args[1]);
+
+        switch(keyType) {
+            case INT32:
+            case INT64:
+            case UINT32:
+            case UINT64:
+            case BOOL:
+            case STRING:
+            case BYTES:
+                // These are OK.
+                break;
+            default:
+                throw context.runtime.newArgumentError("Invalid key type for map.");
+        }
+
+        int initValueArg = 2;
+        if (needTypeclass(this.valueType) && args.length > 2) {
+            this.valueTypeClass = args[2];
+            Utils.validateTypeClass(context, this.valueType, this.valueTypeClass);
+            initValueArg = 3;
+        } else {
+            this.valueTypeClass = context.runtime.getNilClass();
+        }
+
+        // Table value type is always UINT64: this ensures enough space to store the
+        // native_slot value.
+        if (args.length > initValueArg) {
+            mergeIntoSelf(context, args[initValueArg]);
+        }
+        return this;
+    }
+
+    /*
+     * call-seq:
+     *     Map.[]=(key, value) => value
+     *
+     * Inserts or overwrites the value at the given key with the given new value.
+     * Throws an exception if the key type is incorrect. Returns the new value that
+     * was just inserted.
+     */
+    @JRubyMethod(name = "[]=")
+    public IRubyObject indexSet(ThreadContext context, IRubyObject key, IRubyObject value) {
+        Utils.checkType(context, keyType, key, (RubyModule) valueTypeClass);
+        Utils.checkType(context, valueType, value, (RubyModule) valueTypeClass);
+        IRubyObject symbol;
+        if (valueType == Descriptors.FieldDescriptor.Type.ENUM &&
+                Utils.isRubyNum(value) &&
+                ! (symbol = RubyEnum.lookup(context, valueTypeClass, value)).isNil()) {
+            value = symbol;
+        }
+        this.table.put(key, value);
+        return value;
+    }
+
+    /*
+     * call-seq:
+     *     Map.[](key) => value
+     *
+     * Accesses the element at the given key. Throws an exception if the key type is
+     * incorrect. Returns nil when the key is not present in the map.
+     */
+    @JRubyMethod(name = "[]")
+    public IRubyObject index(ThreadContext context, IRubyObject key) {
+        if (table.containsKey(key))
+            return this.table.get(key);
+        return context.runtime.getNil();
+    }
+
+    /*
+     * call-seq:
+     *     Map.==(other) => boolean
+     *
+     * Compares this map to another. Maps are equal if they have identical key sets,
+     * and for each key, the values in both maps compare equal. Elements are
+     * compared as per normal Ruby semantics, by calling their :== methods (or
+     * performing a more efficient comparison for primitive types).
+     *
+     * Maps with dissimilar key types or value types/typeclasses are never equal,
+     * even if value comparison (for example, between integers and floats) would
+     * have otherwise indicated that every element has equal value.
+     */
+    @JRubyMethod(name = "==")
+    public IRubyObject eq(ThreadContext context, IRubyObject _other) {
+        if (_other instanceof RubyHash)
+            return toHash(context).op_equal(context, _other);
+        RubyMap other = (RubyMap) _other;
+        if (this == other) return context.runtime.getTrue();
+        if (!typeCompatible(other) || this.table.size() != other.table.size())
+            return context.runtime.getFalse();
+        for (IRubyObject key : table.keySet()) {
+            if (! other.table.containsKey(key))
+                return context.runtime.getFalse();
+            if (! other.table.get(key).equals(table.get(key)))
+                return context.runtime.getFalse();
+        }
+        return context.runtime.getTrue();
+    }
+
+    /*
+     * call-seq:
+     *     Map.inspect => string
+     *
+     * Returns a string representing this map's elements. It will be formatted as
+     * "{key => value, key => value, ...}", with each key and value string
+     * representation computed by its own #inspect method.
+     */
+    @JRubyMethod
+    public IRubyObject inspect() {
+        return toHash(getRuntime().getCurrentContext()).inspect();
+    }
+
+    /*
+     * call-seq:
+     *     Map.hash => hash_value
+     *
+     * Returns a hash value based on this map's contents.
+     */
+    @JRubyMethod
+    public IRubyObject hash(ThreadContext context) {
+        try {
+            MessageDigest digest = MessageDigest.getInstance("SHA-256");
+            for (IRubyObject key : table.keySet()) {
+                digest.update((byte) key.hashCode());
+                digest.update((byte) table.get(key).hashCode());
+            }
+            return context.runtime.newString(new ByteList(digest.digest()));
+        } catch (NoSuchAlgorithmException ignore) {
+            return context.runtime.newFixnum(System.identityHashCode(table));
+        }
+    }
+
+    /*
+     * call-seq:
+     *     Map.keys => [list_of_keys]
+     *
+     * Returns the list of keys contained in the map, in unspecified order.
+     */
+    @JRubyMethod
+    public IRubyObject keys(ThreadContext context) {
+        return RubyArray.newArray(context.runtime, table.keySet());
+    }
+
+    /*
+     * call-seq:
+     *     Map.values => [list_of_values]
+     *
+     * Returns the list of values contained in the map, in unspecified order.
+     */
+    @JRubyMethod
+    public IRubyObject values(ThreadContext context) {
+        return RubyArray.newArray(context.runtime, table.values());
+    }
+
+    /*
+     * call-seq:
+     *     Map.clear
+     *
+     * Removes all entries from the map.
+     */
+    @JRubyMethod
+    public IRubyObject clear(ThreadContext context) {
+        table.clear();
+        return context.runtime.getNil();
+    }
+
+    /*
+     * call-seq:
+     *     Map.each(&block)
+     *
+     * Invokes &block on each |key, value| pair in the map, in unspecified order.
+     * Note that Map also includes Enumerable; map thus acts like a normal Ruby
+     * sequence.
+     */
+    @JRubyMethod
+    public IRubyObject each(ThreadContext context, Block block) {
+        for (IRubyObject key : table.keySet()) {
+            block.yieldSpecific(context, key, table.get(key));
+        }
+        return context.runtime.getNil();
+    }
+
+    /*
+     * call-seq:
+     *     Map.delete(key) => old_value
+     *
+     * Deletes the value at the given key, if any, returning either the old value or
+     * nil if none was present. Throws an exception if the key is of the wrong type.
+     */
+    @JRubyMethod
+    public IRubyObject delete(ThreadContext context, IRubyObject key) {
+        return table.remove(key);
+    }
+
+    /*
+     * call-seq:
+     *     Map.has_key?(key) => bool
+     *
+     * Returns true if the given key is present in the map. Throws an exception if
+     * the key has the wrong type.
+     */
+    @JRubyMethod(name = "has_key?")
+    public IRubyObject hasKey(ThreadContext context, IRubyObject key) {
+        return this.table.containsKey(key) ? context.runtime.getTrue() : context.runtime.getFalse();
+    }
+
+    /*
+     * call-seq:
+     *     Map.length
+     *
+     * Returns the number of entries (key-value pairs) in the map.
+     */
+    @JRubyMethod
+    public IRubyObject length(ThreadContext context) {
+        return context.runtime.newFixnum(this.table.size());
+    }
+
+    /*
+     * call-seq:
+     *     Map.dup => new_map
+     *
+     * Duplicates this map with a shallow copy. References to all non-primitive
+     * element objects (e.g., submessages) are shared.
+     */
+    @JRubyMethod
+    public IRubyObject dup(ThreadContext context) {
+        RubyMap newMap = newThisType(context);
+        for (Map.Entry<IRubyObject, IRubyObject> entry : table.entrySet()) {
+            newMap.table.put(entry.getKey(), entry.getValue());
+        }
+        return newMap;
+    }
+
+    @JRubyMethod(name = "to_h")
+    public RubyHash toHash(ThreadContext context) {
+        return RubyHash.newHash(context.runtime, table, context.runtime.getNil());
+    }
+
+    // Used by Google::Protobuf.deep_copy but not exposed directly.
+    protected IRubyObject deepCopy(ThreadContext context) {
+        RubyMap newMap = newThisType(context);
+        switch (valueType) {
+            case MESSAGE:
+                for (IRubyObject key : table.keySet()) {
+                    RubyMessage message = (RubyMessage) table.get(key);
+                    newMap.table.put(key.dup(), message.deepCopy(context));
+                }
+                break;
+            default:
+                for (IRubyObject key : table.keySet()) {
+                    newMap.table.put(key.dup(), table.get(key).dup());
+                }
+        }
+        return newMap;
+    }
+
+    protected List<DynamicMessage> build(ThreadContext context, RubyDescriptor descriptor) {
+        List<DynamicMessage> list = new ArrayList<DynamicMessage>();
+        RubyClass rubyClass = (RubyClass) descriptor.msgclass(context);
+        Descriptors.FieldDescriptor keyField = descriptor.lookup("key").getFieldDef();
+        Descriptors.FieldDescriptor valueField = descriptor.lookup("value").getFieldDef();
+        for (IRubyObject key : table.keySet()) {
+            RubyMessage mapMessage = (RubyMessage) rubyClass.newInstance(context, Block.NULL_BLOCK);
+            mapMessage.setField(context, keyField, key);
+            mapMessage.setField(context, valueField, table.get(key));
+            list.add(mapMessage.build(context));
+        }
+        return list;
+    }
+
+    protected RubyMap mergeIntoSelf(final ThreadContext context, IRubyObject hashmap) {
+        if (hashmap instanceof RubyHash) {
+            ((RubyHash) hashmap).visitAll(new RubyHash.Visitor() {
+                @Override
+                public void visit(IRubyObject key, IRubyObject val) {
+                    indexSet(context, key, val);
+                }
+            });
+        } else if (hashmap instanceof RubyMap) {
+            RubyMap other = (RubyMap) hashmap;
+            if (!typeCompatible(other)) {
+                throw context.runtime.newTypeError("Attempt to merge Map with mismatching types");
+            }
+        } else {
+            throw context.runtime.newTypeError("Unknown type merging into Map");
+        }
+        return this;
+    }
+
+    protected boolean typeCompatible(RubyMap other) {
+        return this.keyType == other.keyType &&
+                this.valueType == other.valueType &&
+                this.valueTypeClass == other.valueTypeClass;
+    }
+
+    private RubyMap newThisType(ThreadContext context) {
+        RubyMap newMap;
+        if (needTypeclass(valueType)) {
+            newMap = (RubyMap) metaClass.newInstance(context,
+                    Utils.fieldTypeToRuby(context, keyType),
+                    Utils.fieldTypeToRuby(context, valueType),
+                    valueTypeClass, Block.NULL_BLOCK);
+        } else {
+            newMap = (RubyMap) metaClass.newInstance(context,
+                    Utils.fieldTypeToRuby(context, keyType),
+                    Utils.fieldTypeToRuby(context, valueType),
+                    Block.NULL_BLOCK);
+        }
+        newMap.table = new HashMap<IRubyObject, IRubyObject>();
+        return newMap;
+    }
+
+    private boolean needTypeclass(Descriptors.FieldDescriptor.Type type) {
+        switch(type) {
+            case MESSAGE:
+            case ENUM:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    private Descriptors.FieldDescriptor.Type keyType;
+    private Descriptors.FieldDescriptor.Type valueType;
+    private IRubyObject valueTypeClass;
+    private Map<IRubyObject, IRubyObject> table;
+}
diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyMessage.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyMessage.java
new file mode 100644
index 0000000..04bc0b7
--- /dev/null
+++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyMessage.java
@@ -0,0 +1,744 @@
+/*
+ * Protocol Buffers - Google's data interchange format
+ * Copyright 2014 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.
+ */
+
+package com.google.protobuf.jruby;
+
+import com.google.protobuf.*;
+import org.jruby.*;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.Block;
+import org.jruby.runtime.Helpers;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.jruby.util.ByteList;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class RubyMessage extends RubyObject {
+    public RubyMessage(Ruby ruby, RubyClass klazz, Descriptors.Descriptor descriptor) {
+        super(ruby, klazz);
+        this.descriptor = descriptor;
+    }
+
+    /*
+     * call-seq:
+     *     Message.new(kwargs) => new_message
+     *
+     * Creates a new instance of the given message class. Keyword arguments may be
+     * provided with keywords corresponding to field names.
+     *
+     * Note that no literal Message class exists. Only concrete classes per message
+     * type exist, as provided by the #msgclass method on Descriptors after they
+     * have been added to a pool. The method definitions described here on the
+     * Message class are provided on each concrete message class.
+     */
+    @JRubyMethod(optional = 1)
+    public IRubyObject initialize(final ThreadContext context, IRubyObject[] args) {
+        final Ruby runtime = context.runtime;
+        this.cRepeatedField = (RubyClass) runtime.getClassFromPath("Google::Protobuf::RepeatedField");
+        this.cMap = (RubyClass) runtime.getClassFromPath("Google::Protobuf::Map");
+        this.builder = DynamicMessage.newBuilder(this.descriptor);
+        this.repeatedFields = new HashMap<Descriptors.FieldDescriptor, RubyRepeatedField>();
+        this.maps = new HashMap<Descriptors.FieldDescriptor, RubyMap>();
+        this.fields = new HashMap<Descriptors.FieldDescriptor, IRubyObject>();
+        this.oneofCases = new HashMap<Descriptors.OneofDescriptor, Descriptors.FieldDescriptor>();
+        if (args.length == 1) {
+            if (!(args[0] instanceof RubyHash)) {
+                throw runtime.newArgumentError("expected Hash arguments.");
+            }
+            RubyHash hash = args[0].convertToHash();
+            hash.visitAll(new RubyHash.Visitor() {
+                @Override
+                public void visit(IRubyObject key, IRubyObject value) {
+                    if (!(key instanceof RubySymbol))
+                        throw runtime.newTypeError("Expected symbols as hash keys in initialization map.");
+                    final Descriptors.FieldDescriptor fieldDescriptor = findField(context, key);
+
+                    if (Utils.isMapEntry(fieldDescriptor)) {
+                        if (!(value instanceof RubyHash))
+                            throw runtime.newArgumentError("Expected Hash object as initializer value for map field.");
+
+                        final RubyMap map = newMapForField(context, fieldDescriptor);
+                        map.mergeIntoSelf(context, value);
+                        maps.put(fieldDescriptor, map);
+                    } else if (fieldDescriptor.isRepeated()) {
+                        if (!(value instanceof RubyArray))
+                            throw runtime.newTypeError("Expected array as initializer var for repeated field.");
+                        RubyRepeatedField repeatedField = rubyToRepeatedField(context, fieldDescriptor, value);
+                        addRepeatedField(fieldDescriptor, repeatedField);
+                    } else {
+                        Descriptors.OneofDescriptor oneof = fieldDescriptor.getContainingOneof();
+                        if (oneof != null) {
+                            oneofCases.put(oneof, fieldDescriptor);
+                        }
+                        fields.put(fieldDescriptor, value);
+                    }
+
+                }
+            });
+        }
+        return this;
+    }
+
+    /*
+     * call-seq:
+     *     Message.[]=(index, value)
+     *
+     * Sets a field's value by field name. The provided field name should be a
+     * string.
+     */
+    @JRubyMethod(name = "[]=")
+    public IRubyObject indexSet(ThreadContext context, IRubyObject fieldName, IRubyObject value) {
+        Descriptors.FieldDescriptor fieldDescriptor = findField(context, fieldName);
+        return setField(context, fieldDescriptor, value);
+    }
+
+    /*
+     * call-seq:
+     *     Message.[](index) => value
+     *
+     * Accesses a field's value by field name. The provided field name should be a
+     * string.
+     */
+    @JRubyMethod(name = "[]")
+    public IRubyObject index(ThreadContext context, IRubyObject fieldName) {
+        Descriptors.FieldDescriptor fieldDescriptor = findField(context, fieldName);
+        return getField(context, fieldDescriptor);
+    }
+
+    /*
+     * call-seq:
+     *     Message.inspect => string
+     *
+     * Returns a human-readable string representing this message. It will be
+     * formatted as "<MessageType: field1: value1, field2: value2, ...>". Each
+     * field's value is represented according to its own #inspect method.
+     */
+    @JRubyMethod
+    public IRubyObject inspect() {
+        String cname = metaClass.getName();
+        StringBuilder sb = new StringBuilder("<");
+        sb.append(cname);
+        sb.append(": ");
+        sb.append(this.layoutInspect());
+        sb.append(">");
+
+        return getRuntime().newString(sb.toString());
+    }
+
+    /*
+     * call-seq:
+     *     Message.hash => hash_value
+     *
+     * Returns a hash value that represents this message's field values.
+     */
+    @JRubyMethod
+    public IRubyObject hash(ThreadContext context) {
+        int hashCode = System.identityHashCode(this);
+        return context.runtime.newFixnum(hashCode);
+    }
+
+    /*
+     * call-seq:
+     *     Message.==(other) => boolean
+     *
+     * Performs a deep comparison of this message with another. Messages are equal
+     * if they have the same type and if each field is equal according to the :==
+     * method's semantics (a more efficient comparison may actually be done if the
+     * field is of a primitive type).
+     */
+    @JRubyMethod(name = "==")
+    public IRubyObject eq(ThreadContext context, IRubyObject other) {
+        Ruby runtime = context.runtime;
+        if (!(other instanceof RubyMessage))
+            return runtime.getFalse();
+        RubyMessage message = (RubyMessage) other;
+        if (descriptor != message.descriptor) {
+            return runtime.getFalse();
+        }
+
+        for (Descriptors.FieldDescriptor fdef : descriptor.getFields()) {
+            IRubyObject thisVal = getField(context, fdef);
+            IRubyObject thatVal = message.getField(context, fdef);
+            IRubyObject ret = thisVal.callMethod(context, "==", thatVal);
+            if (!ret.isTrue()) {
+                return runtime.getFalse();
+            }
+        }
+        return runtime.getTrue();
+    }
+
+    /*
+     * call-seq:
+     *     Message.method_missing(*args)
+     *
+     * Provides accessors and setters for message fields according to their field
+     * names. For any field whose name does not conflict with a built-in method, an
+     * accessor is provided with the same name as the field, and a setter is
+     * provided with the name of the field plus the '=' suffix. Thus, given a
+     * message instance 'msg' with field 'foo', the following code is valid:
+     *
+     *     msg.foo = 42
+     *     puts msg.foo
+     */
+    @JRubyMethod(name = "method_missing", rest = true)
+    public IRubyObject methodMissing(ThreadContext context, IRubyObject[] args) {
+        if (args.length == 1) {
+            RubyDescriptor rubyDescriptor = (RubyDescriptor) getDescriptor(context, metaClass);
+            IRubyObject oneofDescriptor = rubyDescriptor.lookupOneof(context, args[0]);
+            if (oneofDescriptor.isNil()) {
+                return index(context, args[0]);
+            }
+            RubyOneofDescriptor rubyOneofDescriptor = (RubyOneofDescriptor) oneofDescriptor;
+            Descriptors.FieldDescriptor fieldDescriptor =
+                    oneofCases.get(rubyOneofDescriptor.getOneofDescriptor());
+            if (fieldDescriptor == null)
+                return context.runtime.getNil();
+
+            return context.runtime.newSymbol(fieldDescriptor.getName());
+        } else {
+            // fieldName is RubySymbol
+            RubyString field = args[0].asString();
+            RubyString equalSign = context.runtime.newString(Utils.EQUAL_SIGN);
+            if (field.end_with_p(context, equalSign).isTrue()) {
+                field.chomp_bang(context, equalSign);
+            }
+            return indexSet(context, field, args[1]);
+        }
+    }
+
+    /**
+     * call-seq:
+     * Message.dup => new_message
+     * Performs a shallow copy of this message and returns the new copy.
+     */
+    @JRubyMethod
+    public IRubyObject dup(ThreadContext context) {
+        RubyMessage dup = (RubyMessage) metaClass.newInstance(context, Block.NULL_BLOCK);
+        IRubyObject value;
+        for (Descriptors.FieldDescriptor fieldDescriptor : builder.getAllFields().keySet()) {
+            if (fieldDescriptor.isRepeated()) {
+                dup.repeatedFields.put(fieldDescriptor, getRepeatedField(context, fieldDescriptor));
+            } else if (builder.hasField(fieldDescriptor)) {
+                dup.fields.put(fieldDescriptor, wrapField(context, fieldDescriptor, builder.getField(fieldDescriptor)));
+            }
+        }
+        for (Descriptors.FieldDescriptor fieldDescriptor : fields.keySet()) {
+            dup.fields.put(fieldDescriptor, fields.get(fieldDescriptor));
+        }
+        for (Descriptors.FieldDescriptor fieldDescriptor : maps.keySet()) {
+            dup.maps.put(fieldDescriptor, maps.get(fieldDescriptor));
+        }
+        return dup;
+    }
+
+    /*
+     * call-seq:
+     *     Message.descriptor => descriptor
+     *
+     * Class method that returns the Descriptor instance corresponding to this
+     * message class's type.
+     */
+    @JRubyMethod(name = "descriptor", meta = true)
+    public static IRubyObject getDescriptor(ThreadContext context, IRubyObject recv) {
+        return ((RubyClass) recv).getInstanceVariable(Utils.DESCRIPTOR_INSTANCE_VAR);
+    }
+
+    /*
+     * call-seq:
+     *     MessageClass.encode(msg) => bytes
+     *
+     * Encodes the given message object to its serialized form in protocol buffers
+     * wire format.
+     */
+    @JRubyMethod(meta = true)
+    public static IRubyObject encode(ThreadContext context, IRubyObject recv, IRubyObject value) {
+        RubyMessage message = (RubyMessage) value;
+        return context.runtime.newString(new ByteList(message.build(context).toByteArray()));
+    }
+
+    /*
+     * call-seq:
+     *     MessageClass.decode(data) => message
+     *
+     * Decodes the given data (as a string containing bytes in protocol buffers wire
+     * format) under the interpretration given by this message class's definition
+     * and returns a message object with the corresponding field values.
+     */
+    @JRubyMethod(meta = true)
+    public static IRubyObject decode(ThreadContext context, IRubyObject recv, IRubyObject data) {
+        byte[] bin = data.convertToString().getBytes();
+        RubyMessage ret = (RubyMessage) ((RubyClass) recv).newInstance(context, Block.NULL_BLOCK);
+        try {
+            ret.builder.mergeFrom(bin);
+        } catch (InvalidProtocolBufferException e) {
+            throw context.runtime.newRuntimeError(e.getMessage());
+        }
+        return ret;
+    }
+
+    /*
+     * call-seq:
+     *     MessageClass.encode_json(msg) => json_string
+     *
+     * Encodes the given message object into its serialized JSON representation.
+     */
+    @JRubyMethod(name = "encode_json", meta = true)
+    public static IRubyObject encodeJson(ThreadContext context, IRubyObject recv, IRubyObject msgRb) {
+        RubyMessage message = (RubyMessage) msgRb;
+        return Helpers.invoke(context, message.toHash(context), "to_json");
+    }
+
+    /*
+     * call-seq:
+     *     MessageClass.decode_json(data) => message
+     *
+     * Decodes the given data (as a string containing bytes in protocol buffers wire
+     * format) under the interpretration given by this message class's definition
+     * and returns a message object with the corresponding field values.
+     */
+    @JRubyMethod(name = "decode_json", meta = true)
+    public static IRubyObject decodeJson(ThreadContext context, IRubyObject recv, IRubyObject json) {
+        Ruby runtime = context.runtime;
+        RubyMessage ret = (RubyMessage) ((RubyClass) recv).newInstance(context, Block.NULL_BLOCK);
+        RubyModule jsonModule = runtime.getClassFromPath("JSON");
+        RubyHash opts = RubyHash.newHash(runtime);
+        opts.fastASet(runtime.newSymbol("symbolize_names"), runtime.getTrue());
+        IRubyObject[] args = new IRubyObject[] { Helpers.invoke(context, jsonModule, "parse", json, opts) };
+        ret.initialize(context, args);
+        return ret;
+    }
+
+    @JRubyMethod(name = "to_h")
+    public IRubyObject toHash(ThreadContext context) {
+        Ruby runtime = context.runtime;
+        RubyHash ret = RubyHash.newHash(runtime);
+        for (Descriptors.FieldDescriptor fdef : this.descriptor.getFields()) {
+            IRubyObject value = getField(context, fdef);
+            if (value.respondsTo("to_h")) {
+                value = Helpers.invoke(context, value, "to_h");
+            }
+            ret.fastASet(runtime.newString(fdef.getName()), value);
+        }
+        return ret;
+    }
+
+    protected DynamicMessage build(ThreadContext context) {
+        return build(context, 0);
+    }
+
+    protected DynamicMessage build(ThreadContext context, int depth) {
+        if (depth > SINK_MAXIMUM_NESTING) {
+            throw context.runtime.newRuntimeError("Maximum recursion depth exceeded during encoding.");
+        }
+        for (Descriptors.FieldDescriptor fieldDescriptor : maps.keySet()) {
+            this.builder.clearField(fieldDescriptor);
+            RubyDescriptor mapDescriptor = (RubyDescriptor) getDescriptorForField(context, fieldDescriptor);
+            for (DynamicMessage kv : maps.get(fieldDescriptor).build(context, mapDescriptor)) {
+                this.builder.addRepeatedField(fieldDescriptor, kv);
+            }
+        }
+        for (Descriptors.FieldDescriptor fieldDescriptor : repeatedFields.keySet()) {
+            RubyRepeatedField repeatedField = repeatedFields.get(fieldDescriptor);
+            this.builder.clearField(fieldDescriptor);
+            for (int i = 0; i < repeatedField.size(); i++) {
+                Object item = convert(context, fieldDescriptor, repeatedField.get(i), depth);
+                this.builder.addRepeatedField(fieldDescriptor, item);
+            }
+        }
+        for (Descriptors.FieldDescriptor fieldDescriptor : fields.keySet()) {
+            IRubyObject value = fields.get(fieldDescriptor);
+            this.builder.setField(fieldDescriptor, convert(context, fieldDescriptor, value, depth));
+        }
+        return this.builder.build();
+    }
+
+    protected Descriptors.Descriptor getDescriptor() {
+        return this.descriptor;
+    }
+
+    // Internal use only, called by Google::Protobuf.deep_copy
+    protected IRubyObject deepCopy(ThreadContext context) {
+        RubyMessage copy = (RubyMessage) metaClass.newInstance(context, Block.NULL_BLOCK);
+        for (Descriptors.FieldDescriptor fdef : this.descriptor.getFields()) {
+            if (fdef.isRepeated()) {
+                copy.addRepeatedField(fdef, this.getRepeatedField(context, fdef).deepCopy(context));
+            } else if (fields.containsKey(fdef)) {
+                copy.fields.put(fdef, fields.get(fdef));
+            } else if (this.builder.hasField(fdef)) {
+                copy.fields.put(fdef, wrapField(context, fdef, this.builder.getField(fdef)));
+            }
+        }
+        return copy;
+    }
+
+    private RubyRepeatedField getRepeatedField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor) {
+        if (this.repeatedFields.containsKey(fieldDescriptor)) {
+            return this.repeatedFields.get(fieldDescriptor);
+        }
+        int count = this.builder.getRepeatedFieldCount(fieldDescriptor);
+        RubyRepeatedField ret = repeatedFieldForFieldDescriptor(context, fieldDescriptor);
+        for (int i = 0; i < count; i++) {
+            ret.push(context, wrapField(context, fieldDescriptor, this.builder.getRepeatedField(fieldDescriptor, i)));
+        }
+        return ret;
+    }
+
+    private void addRepeatedField(Descriptors.FieldDescriptor fieldDescriptor, RubyRepeatedField repeatedField) {
+        this.repeatedFields.put(fieldDescriptor, repeatedField);
+    }
+
+    private IRubyObject buildFrom(ThreadContext context, DynamicMessage dynamicMessage) {
+        this.builder.mergeFrom(dynamicMessage);
+        return this;
+    }
+
+    private Descriptors.FieldDescriptor findField(ThreadContext context, IRubyObject fieldName) {
+        String nameStr = fieldName.asJavaString();
+        Descriptors.FieldDescriptor ret = this.descriptor.findFieldByName(Utils.escapeIdentifier(nameStr));
+        if (ret == null)
+            throw context.runtime.newArgumentError("field " + fieldName.asJavaString() + " is not found");
+        return ret;
+    }
+
+    private void checkRepeatedFieldType(ThreadContext context, IRubyObject value,
+                                        Descriptors.FieldDescriptor fieldDescriptor) {
+        Ruby runtime = context.runtime;
+        if (!(value instanceof RubyRepeatedField)) {
+            throw runtime.newTypeError("Expected repeated field array");
+        }
+    }
+
+    // convert a ruby object to protobuf type, with type check
+    private Object convert(ThreadContext context,
+                           Descriptors.FieldDescriptor fieldDescriptor,
+                           IRubyObject value, int depth) {
+        Ruby runtime = context.runtime;
+        Object val = null;
+        switch (fieldDescriptor.getType()) {
+            case INT32:
+            case INT64:
+            case UINT32:
+            case UINT64:
+                if (!Utils.isRubyNum(value)) {
+                    throw runtime.newTypeError("Expected number type for integral field.");
+                }
+                Utils.checkIntTypePrecision(context, fieldDescriptor.getType(), value);
+                switch (fieldDescriptor.getType()) {
+                    case INT32:
+                        val = RubyNumeric.num2int(value);
+                        break;
+                    case INT64:
+                        val = RubyNumeric.num2long(value);
+                        break;
+                    case UINT32:
+                        val = Utils.num2uint(value);
+                        break;
+                    case UINT64:
+                        val = Utils.num2ulong(context.runtime, value);
+                        break;
+                    default:
+                        break;
+                }
+                break;
+            case FLOAT:
+                if (!Utils.isRubyNum(value))
+                    throw runtime.newTypeError("Expected number type for float field.");
+                val = (float) RubyNumeric.num2dbl(value);
+                break;
+            case DOUBLE:
+                if (!Utils.isRubyNum(value))
+                    throw runtime.newTypeError("Expected number type for double field.");
+                val = RubyNumeric.num2dbl(value);
+                break;
+            case BOOL:
+                if (!(value instanceof RubyBoolean))
+                    throw runtime.newTypeError("Invalid argument for boolean field.");
+                val = value.isTrue();
+                break;
+            case BYTES:
+            case STRING:
+                Utils.validateStringEncoding(context.runtime, fieldDescriptor.getType(), value);
+                RubyString str = (RubyString) value;
+                switch (fieldDescriptor.getType()) {
+                    case BYTES:
+                        val = ByteString.copyFrom(str.getBytes());
+                        break;
+                    case STRING:
+                        val = str.asJavaString();
+                        break;
+                    default:
+                        break;
+                }
+                break;
+            case MESSAGE:
+                RubyClass typeClass = (RubyClass) ((RubyDescriptor) getDescriptorForField(context, fieldDescriptor)).msgclass(context);
+                if (!value.getMetaClass().equals(typeClass))
+                    throw runtime.newTypeError(value, "Invalid type to assign to submessage field.");
+                val = ((RubyMessage) value).build(context, depth + 1);
+                break;
+            case ENUM:
+                Descriptors.EnumDescriptor enumDescriptor = fieldDescriptor.getEnumType();
+
+                if (Utils.isRubyNum(value)) {
+                    val = enumDescriptor.findValueByNumberCreatingIfUnknown(RubyNumeric.num2int(value));
+                } else if (value instanceof RubySymbol) {
+                    val = enumDescriptor.findValueByName(value.asJavaString());
+                } else {
+                    throw runtime.newTypeError("Expected number or symbol type for enum field.");
+                }
+                if (val == null) {
+                    throw runtime.newRangeError("Enum value " + value + " is not found.");
+                }
+                break;
+            default:
+                break;
+        }
+        return val;
+    }
+
+    private IRubyObject wrapField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor, Object value) {
+        if (value == null) {
+            return context.runtime.getNil();
+        }
+        Ruby runtime = context.runtime;
+        switch (fieldDescriptor.getType()) {
+            case INT32:
+            case INT64:
+            case UINT32:
+            case UINT64:
+            case FLOAT:
+            case DOUBLE:
+            case BOOL:
+            case BYTES:
+            case STRING:
+                return Utils.wrapPrimaryValue(context, fieldDescriptor.getType(), value);
+            case MESSAGE:
+                RubyClass typeClass = (RubyClass) ((RubyDescriptor) getDescriptorForField(context, fieldDescriptor)).msgclass(context);
+                RubyMessage msg = (RubyMessage) typeClass.newInstance(context, Block.NULL_BLOCK);
+                return msg.buildFrom(context, (DynamicMessage) value);
+            case ENUM:
+                Descriptors.EnumValueDescriptor enumValueDescriptor = (Descriptors.EnumValueDescriptor) value;
+                if (enumValueDescriptor.getIndex() == -1) { // UNKNOWN ENUM VALUE
+                    return runtime.newFixnum(enumValueDescriptor.getNumber());
+                }
+                return runtime.newSymbol(enumValueDescriptor.getName());
+            default:
+                return runtime.newString(value.toString());
+        }
+    }
+
+    private RubyRepeatedField repeatedFieldForFieldDescriptor(ThreadContext context,
+                                                              Descriptors.FieldDescriptor fieldDescriptor) {
+        IRubyObject typeClass = context.runtime.getNilClass();
+
+        IRubyObject descriptor = getDescriptorForField(context, fieldDescriptor);
+        Descriptors.FieldDescriptor.Type type = fieldDescriptor.getType();
+        if (type == Descriptors.FieldDescriptor.Type.MESSAGE) {
+            typeClass = ((RubyDescriptor) descriptor).msgclass(context);
+
+        } else if (type == Descriptors.FieldDescriptor.Type.ENUM) {
+            typeClass = ((RubyEnumDescriptor) descriptor).enummodule(context);
+        }
+        return new RubyRepeatedField(context.runtime, cRepeatedField, type, typeClass);
+    }
+
+    protected IRubyObject getField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor) {
+        Descriptors.OneofDescriptor oneofDescriptor = fieldDescriptor.getContainingOneof();
+        if (oneofDescriptor != null) {
+            if (oneofCases.containsKey(oneofDescriptor)) {
+                if (oneofCases.get(oneofDescriptor) != fieldDescriptor)
+                    return context.runtime.getNil();
+                return fields.get(fieldDescriptor);
+            } else {
+                Descriptors.FieldDescriptor oneofCase = builder.getOneofFieldDescriptor(oneofDescriptor);
+                if (oneofCase != fieldDescriptor) return context.runtime.getNil();
+                IRubyObject value = wrapField(context, oneofCase, builder.getField(oneofCase));
+                fields.put(fieldDescriptor, value);
+                return value;
+            }
+        }
+
+        if (Utils.isMapEntry(fieldDescriptor)) {
+            RubyMap map = maps.get(fieldDescriptor);
+            if (map == null) {
+                map = newMapForField(context, fieldDescriptor);
+                int mapSize = this.builder.getRepeatedFieldCount(fieldDescriptor);
+                Descriptors.FieldDescriptor keyField = fieldDescriptor.getMessageType().findFieldByNumber(1);
+                Descriptors.FieldDescriptor valueField = fieldDescriptor.getMessageType().findFieldByNumber(2);
+                RubyDescriptor kvDescriptor = (RubyDescriptor) getDescriptorForField(context, fieldDescriptor);
+                RubyClass kvClass = (RubyClass) kvDescriptor.msgclass(context);
+                for (int i = 0; i < mapSize; i++) {
+                    RubyMessage kvMessage = (RubyMessage) kvClass.newInstance(context, Block.NULL_BLOCK);
+                    DynamicMessage message = (DynamicMessage) this.builder.getRepeatedField(fieldDescriptor, i);
+                    kvMessage.buildFrom(context, message);
+                    map.indexSet(context, kvMessage.getField(context, keyField), kvMessage.getField(context, valueField));
+                }
+                maps.put(fieldDescriptor, map);
+            }
+            return map;
+        }
+        if (fieldDescriptor.isRepeated()) {
+            return getRepeatedField(context, fieldDescriptor);
+        }
+        if (fieldDescriptor.getType() != Descriptors.FieldDescriptor.Type.MESSAGE ||
+                this.builder.hasField(fieldDescriptor) || fields.containsKey(fieldDescriptor)) {
+            if (fields.containsKey(fieldDescriptor)) {
+                return fields.get(fieldDescriptor);
+            } else {
+                IRubyObject value = wrapField(context, fieldDescriptor, this.builder.getField(fieldDescriptor));
+                if (this.builder.hasField(fieldDescriptor)) {
+                    fields.put(fieldDescriptor, value);
+                }
+                return value;
+            }
+        }
+        return context.runtime.getNil();
+    }
+
+    protected IRubyObject setField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor, IRubyObject value) {
+        if (Utils.isMapEntry(fieldDescriptor)) {
+            if (!(value instanceof RubyMap)) {
+                throw context.runtime.newTypeError("Expected Map instance");
+            }
+            RubyMap thisMap = (RubyMap) getField(context, fieldDescriptor);
+            thisMap.mergeIntoSelf(context, value);
+        } else if (fieldDescriptor.isRepeated()) {
+            checkRepeatedFieldType(context, value, fieldDescriptor);
+            if (value instanceof RubyRepeatedField) {
+                addRepeatedField(fieldDescriptor, (RubyRepeatedField) value);
+            } else {
+                RubyArray ary = value.convertToArray();
+                RubyRepeatedField repeatedField = rubyToRepeatedField(context, fieldDescriptor, ary);
+                addRepeatedField(fieldDescriptor, repeatedField);
+            }
+        } else {
+            Descriptors.OneofDescriptor oneofDescriptor = fieldDescriptor.getContainingOneof();
+            if (oneofDescriptor != null) {
+                Descriptors.FieldDescriptor oneofCase = oneofCases.get(oneofDescriptor);
+                if (oneofCase != null && oneofCase != fieldDescriptor) {
+                    fields.remove(oneofCase);
+                }
+                if (value.isNil()) {
+                    oneofCases.remove(oneofDescriptor);
+                    fields.remove(fieldDescriptor);
+                } else {
+                    oneofCases.put(oneofDescriptor, fieldDescriptor);
+                    fields.put(fieldDescriptor, value);
+                }
+            } else {
+                Descriptors.FieldDescriptor.Type fieldType = fieldDescriptor.getType();
+                IRubyObject typeClass = context.runtime.getObject();
+                if (fieldType == Descriptors.FieldDescriptor.Type.MESSAGE) {
+                    typeClass = ((RubyDescriptor) getDescriptorForField(context, fieldDescriptor)).msgclass(context);
+                } else if (fieldType == Descriptors.FieldDescriptor.Type.ENUM) {
+                    typeClass = ((RubyEnumDescriptor) getDescriptorForField(context, fieldDescriptor)).enummodule(context);
+                }
+                Utils.checkType(context, fieldType, value, (RubyModule) typeClass);
+                // Convert integer enum to symbol
+                if (fieldType == Descriptors.FieldDescriptor.Type.ENUM) {
+                    Descriptors.EnumDescriptor enumDescriptor = fieldDescriptor.getEnumType();
+                    if (Utils.isRubyNum(value)) {
+                        Descriptors.EnumValueDescriptor val =
+                                enumDescriptor.findValueByNumberCreatingIfUnknown(RubyNumeric.num2int(value));
+                        if (val.getIndex() != -1) value = context.runtime.newSymbol(val.getName());
+                    }
+                }
+                this.fields.put(fieldDescriptor, value);
+            }
+        }
+        return context.runtime.getNil();
+    }
+
+    private String layoutInspect() {
+        ThreadContext context = getRuntime().getCurrentContext();
+        StringBuilder sb = new StringBuilder();
+        for (Descriptors.FieldDescriptor fdef : descriptor.getFields()) {
+            sb.append(Utils.unescapeIdentifier(fdef.getName()));
+            sb.append(": ");
+            sb.append(getField(context, fdef).inspect());
+            sb.append(", ");
+        }
+        return sb.substring(0, sb.length() - 2);
+    }
+
+    private IRubyObject getDescriptorForField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor) {
+        RubyDescriptor thisRbDescriptor = (RubyDescriptor) getDescriptor(context, metaClass);
+        return thisRbDescriptor.lookup(fieldDescriptor.getName()).getSubType(context);
+    }
+
+    private RubyRepeatedField rubyToRepeatedField(ThreadContext context,
+                                                  Descriptors.FieldDescriptor fieldDescriptor, IRubyObject value) {
+        RubyArray arr = value.convertToArray();
+        RubyRepeatedField repeatedField = repeatedFieldForFieldDescriptor(context, fieldDescriptor);
+        for (int i = 0; i < arr.size(); i++) {
+            repeatedField.push(context, arr.eltInternal(i));
+        }
+        return repeatedField;
+    }
+
+    private RubyMap newMapForField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor) {
+        RubyDescriptor mapDescriptor = (RubyDescriptor) getDescriptorForField(context, fieldDescriptor);
+        Descriptors.FieldDescriptor keyField = fieldDescriptor.getMessageType().findFieldByNumber(1);
+        Descriptors.FieldDescriptor valueField = fieldDescriptor.getMessageType().findFieldByNumber(2);
+        IRubyObject keyType = RubySymbol.newSymbol(context.runtime, keyField.getType().name());
+        IRubyObject valueType = RubySymbol.newSymbol(context.runtime, valueField.getType().name());
+        if (valueField.getType() == Descriptors.FieldDescriptor.Type.MESSAGE) {
+            RubyFieldDescriptor rubyFieldDescriptor = (RubyFieldDescriptor) mapDescriptor.lookup(context,
+                    context.runtime.newString("value"));
+            RubyDescriptor rubyDescriptor = (RubyDescriptor) rubyFieldDescriptor.getSubType(context);
+            return (RubyMap) cMap.newInstance(context, keyType, valueType,
+                    rubyDescriptor.msgclass(context), Block.NULL_BLOCK);
+        } else {
+            return (RubyMap) cMap.newInstance(context, keyType, valueType, Block.NULL_BLOCK);
+        }
+    }
+
+    private Descriptors.FieldDescriptor getOneofCase(Descriptors.OneofDescriptor oneof) {
+        if (oneofCases.containsKey(oneof)) {
+            return oneofCases.get(oneof);
+        }
+        return builder.getOneofFieldDescriptor(oneof);
+    }
+
+    private Descriptors.Descriptor descriptor;
+    private DynamicMessage.Builder builder;
+    private RubyClass cRepeatedField;
+    private RubyClass cMap;
+    private Map<Descriptors.FieldDescriptor, RubyRepeatedField> repeatedFields;
+    private Map<Descriptors.FieldDescriptor, RubyMap> maps;
+    private Map<Descriptors.FieldDescriptor, IRubyObject> fields;
+    private Map<Descriptors.OneofDescriptor, Descriptors.FieldDescriptor> oneofCases;
+
+    private static final int SINK_MAXIMUM_NESTING = 64;
+}
diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyMessageBuilderContext.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyMessageBuilderContext.java
new file mode 100644
index 0000000..a619b80
--- /dev/null
+++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyMessageBuilderContext.java
@@ -0,0 +1,217 @@
+/*
+ * Protocol Buffers - Google's data interchange format
+ * Copyright 2014 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.
+ */
+
+package com.google.protobuf.jruby;
+
+import com.google.protobuf.Descriptors;
+import org.jruby.*;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.Binding;
+import org.jruby.runtime.Block;
+import org.jruby.runtime.ObjectAllocator;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+
+@JRubyClass(name = "MessageBuilderContext")
+public class RubyMessageBuilderContext extends RubyObject {
+    public static void createRubyMessageBuilderContext(Ruby runtime) {
+        RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf");
+        RubyClass cMessageBuilderContext = protobuf.defineClassUnder("MessageBuilderContext", runtime.getObject(), new ObjectAllocator() {
+            @Override
+            public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+                return new RubyMessageBuilderContext(runtime, klazz);
+            }
+        });
+        cMessageBuilderContext.defineAnnotatedMethods(RubyMessageBuilderContext.class);
+    }
+
+    public RubyMessageBuilderContext(Ruby ruby, RubyClass klazz) {
+        super(ruby, klazz);
+    }
+
+    @JRubyMethod
+    public IRubyObject initialize(ThreadContext context, IRubyObject descriptor, IRubyObject rubyBuilder) {
+        this.cFieldDescriptor = (RubyClass) context.runtime.getClassFromPath("Google::Protobuf::FieldDescriptor");
+        this.cDescriptor = (RubyClass) context.runtime.getClassFromPath("Google::Protobuf::Descriptor");
+        this.cOneofDescriptor = (RubyClass) context.runtime.getClassFromPath("Google::Protobuf::OneofDescriptor");
+        this.cOneofBuilderContext = (RubyClass) context.runtime.getClassFromPath("Google::Protobuf::Internal::OneofBuilderContext");
+        this.descriptor = (RubyDescriptor) descriptor;
+        this.builder = (RubyBuilder) rubyBuilder;
+        return this;
+    }
+
+    /*
+     * call-seq:
+     *     MessageBuilderContext.optional(name, type, number, type_class = nil)
+     *
+     * Defines a new optional field on this message type with the given type, tag
+     * number, and type class (for message and enum fields). The type must be a Ruby
+     * symbol (as accepted by FieldDescriptor#type=) and the type_class must be a
+     * string, if present (as accepted by FieldDescriptor#submsg_name=).
+     */
+    @JRubyMethod(required = 3, optional = 1)
+    public IRubyObject optional(ThreadContext context, IRubyObject[] args) {
+        Ruby runtime = context.runtime;
+        IRubyObject typeClass = runtime.getNil();
+        if (args.length > 3) typeClass = args[3];
+        msgdefAddField(context, "optional", args[0], args[1], args[2], typeClass);
+        return context.runtime.getNil();
+    }
+
+    /*
+     * call-seq:
+     *     MessageBuilderContext.required(name, type, number, type_class = nil)
+     *
+     * Defines a new required field on this message type with the given type, tag
+     * number, and type class (for message and enum fields). The type must be a Ruby
+     * symbol (as accepted by FieldDescriptor#type=) and the type_class must be a
+     * string, if present (as accepted by FieldDescriptor#submsg_name=).
+     *
+     * Proto3 does not have required fields, but this method exists for
+     * completeness. Any attempt to add a message type with required fields to a
+     * pool will currently result in an error.
+     */
+    @JRubyMethod(required = 3, optional = 1)
+    public IRubyObject required(ThreadContext context, IRubyObject[] args) {
+        IRubyObject typeClass = context.runtime.getNil();
+        if (args.length > 3) typeClass = args[3];
+        msgdefAddField(context, "required", args[0], args[1], args[2], typeClass);
+        return context.runtime.getNil();
+    }
+
+    /*
+     * call-seq:
+     *     MessageBuilderContext.repeated(name, type, number, type_class = nil)
+     *
+     * Defines a new repeated field on this message type with the given type, tag
+     * number, and type class (for message and enum fields). The type must be a Ruby
+     * symbol (as accepted by FieldDescriptor#type=) and the type_class must be a
+     * string, if present (as accepted by FieldDescriptor#submsg_name=).
+     */
+    @JRubyMethod(required = 3, optional = 1)
+    public IRubyObject repeated(ThreadContext context, IRubyObject[] args) {
+        IRubyObject typeClass = context.runtime.getNil();
+        if (args.length > 3) typeClass = args[3];
+        msgdefAddField(context, "repeated", args[0], args[1], args[2], typeClass);
+        return context.runtime.getNil();
+    }
+
+    /*
+     * call-seq:
+     *     MessageBuilderContext.map(name, key_type, value_type, number,
+     *                               value_type_class = nil)
+     *
+     * Defines a new map field on this message type with the given key and value
+     * types, tag number, and type class (for message and enum value types). The key
+     * type must be :int32/:uint32/:int64/:uint64, :bool, or :string. The value type
+     * type must be a Ruby symbol (as accepted by FieldDescriptor#type=) and the
+     * type_class must be a string, if present (as accepted by
+     * FieldDescriptor#submsg_name=).
+     */
+    @JRubyMethod(required = 4, optional = 1)
+    public IRubyObject map(ThreadContext context, IRubyObject[] args) {
+        Ruby runtime = context.runtime;
+        IRubyObject name = args[0];
+        IRubyObject keyType = args[1];
+        IRubyObject valueType = args[2];
+        IRubyObject number = args[3];
+        IRubyObject typeClass = args.length > 4 ? args[4] : context.runtime.getNil();
+
+        // Validate the key type. We can't accept enums, messages, or floats/doubles
+        // as map keys. (We exclude these explicitly, and the field-descriptor setter
+        // below then ensures that the type is one of the remaining valid options.)
+        if (keyType.equals(RubySymbol.newSymbol(runtime, "float")) ||
+                keyType.equals(RubySymbol.newSymbol(runtime, "double")) ||
+                keyType.equals(RubySymbol.newSymbol(runtime, "enum")) ||
+                keyType.equals(RubySymbol.newSymbol(runtime, "message")))
+            throw runtime.newArgumentError("Cannot add a map field with a float, double, enum, or message type.");
+
+        // Create a new message descriptor for the map entry message, and create a
+        // repeated submessage field here with that type.
+        RubyDescriptor mapentryDesc = (RubyDescriptor) cDescriptor.newInstance(context, Block.NULL_BLOCK);
+        IRubyObject mapentryDescName = RubySymbol.newSymbol(runtime, name).id2name(context);
+        mapentryDesc.setName(context, mapentryDescName);
+        mapentryDesc.setMapEntry(true);
+
+        //optional <type> key = 1;
+        RubyFieldDescriptor keyField = (RubyFieldDescriptor) cFieldDescriptor.newInstance(context, Block.NULL_BLOCK);
+        keyField.setName(context, runtime.newString("key"));
+        keyField.setLabel(context, RubySymbol.newSymbol(runtime, "optional"));
+        keyField.setNumber(context, runtime.newFixnum(1));
+        keyField.setType(context, keyType);
+        mapentryDesc.addField(context, keyField);
+
+        //optional <type> value = 2;
+        RubyFieldDescriptor valueField = (RubyFieldDescriptor) cFieldDescriptor.newInstance(context, Block.NULL_BLOCK);
+        valueField.setName(context, runtime.newString("value"));
+        valueField.setLabel(context, RubySymbol.newSymbol(runtime, "optional"));
+        valueField.setNumber(context, runtime.newFixnum(2));
+        valueField.setType(context, valueType);
+        if (! typeClass.isNil()) valueField.setSubmsgName(context, typeClass);
+        mapentryDesc.addField(context, valueField);
+
+        // Add the map-entry message type to the current builder, and use the type to
+        // create the map field itself.
+        this.builder.pendingList.add(mapentryDesc);
+
+        msgdefAddField(context, "repeated", name, runtime.newSymbol("message"), number, mapentryDescName);
+        return runtime.getNil();
+    }
+
+    @JRubyMethod
+    public IRubyObject oneof(ThreadContext context, IRubyObject name, Block block) {
+        RubyOneofDescriptor oneofdef = (RubyOneofDescriptor)
+                cOneofDescriptor.newInstance(context, Block.NULL_BLOCK);
+        RubyOneofBuilderContext ctx = (RubyOneofBuilderContext)
+                cOneofBuilderContext.newInstance(context, oneofdef, Block.NULL_BLOCK);
+        oneofdef.setName(context, name);
+        Binding binding = block.getBinding();
+        binding.setSelf(ctx);
+        block.yieldSpecific(context);
+        descriptor.addOneof(context, oneofdef);
+        return context.runtime.getNil();
+    }
+
+    private void msgdefAddField(ThreadContext context, String label, IRubyObject name,
+                                IRubyObject type, IRubyObject number, IRubyObject typeClass) {
+        descriptor.addField(context,
+                Utils.msgdefCreateField(context, label, name, type, number, typeClass, cFieldDescriptor));
+    }
+
+    private RubyDescriptor descriptor;
+    private RubyBuilder builder;
+    private RubyClass cFieldDescriptor;
+    private RubyClass cOneofDescriptor;
+    private RubyClass cOneofBuilderContext;
+    private RubyClass cDescriptor;
+}
diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyOneofBuilderContext.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyOneofBuilderContext.java
new file mode 100644
index 0000000..c9b99e0
--- /dev/null
+++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyOneofBuilderContext.java
@@ -0,0 +1,84 @@
+/*
+ * Protocol Buffers - Google's data interchange format
+ * Copyright 2014 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.
+ */
+
+package com.google.protobuf.jruby;
+
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.RubyModule;
+import org.jruby.RubyObject;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.ObjectAllocator;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+
+@JRubyClass(name = "OneofBuilderContext")
+public class RubyOneofBuilderContext extends RubyObject {
+    public static void createRubyOneofBuilderContext(Ruby runtime) {
+        RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf");
+        RubyModule internal = protobuf.defineModuleUnder("Internal");
+        RubyClass cRubyOneofBuidlerContext = internal.defineClassUnder("OneofBuilderContext", runtime.getObject(), new ObjectAllocator() {
+            @Override
+            public IRubyObject allocate(Ruby ruby, RubyClass rubyClass) {
+                return new RubyOneofBuilderContext(ruby, rubyClass);
+            }
+        });
+        cRubyOneofBuidlerContext.defineAnnotatedMethods(RubyOneofBuilderContext.class);
+    }
+
+    public RubyOneofBuilderContext(Ruby ruby, RubyClass rubyClass) {
+        super(ruby, rubyClass);
+    }
+
+    @JRubyMethod
+    public IRubyObject initialize(ThreadContext context, IRubyObject oneofdef) {
+        this.descriptor = (RubyOneofDescriptor) oneofdef;
+        this.cFieldDescriptor = (RubyClass) context.runtime.getClassFromPath("Google::Protobuf::FieldDescriptor");
+        return this;
+    }
+
+    @JRubyMethod(required = 3, optional = 1)
+    public IRubyObject optional(ThreadContext context, IRubyObject[] args) {
+        IRubyObject name = args[0];
+        IRubyObject type = args[1];
+        IRubyObject number = args[2];
+        IRubyObject typeClass = args.length > 3 ? args[3] : context.runtime.getNil();
+        RubyFieldDescriptor fieldDescriptor = Utils.msgdefCreateField(context, "optional",
+                name, type, number, typeClass, cFieldDescriptor);
+        descriptor.addField(context, fieldDescriptor);
+        return this;
+    }
+
+    private RubyOneofDescriptor descriptor;
+    private RubyClass cFieldDescriptor;
+}
diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyOneofDescriptor.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyOneofDescriptor.java
new file mode 100644
index 0000000..cc4ab66
--- /dev/null
+++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyOneofDescriptor.java
@@ -0,0 +1,124 @@
+package com.google.protobuf.jruby;
+
+import com.google.protobuf.DescriptorProtos;
+import com.google.protobuf.Descriptors;
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.RubyModule;
+import org.jruby.RubyObject;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.Block;
+import org.jruby.runtime.ObjectAllocator;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+
+import java.util.*;
+
+@JRubyClass(name = "OneofDescriptor", include = "Enumerable")
+public class RubyOneofDescriptor extends RubyObject {
+
+    public static void createRubyOneofDescriptor(Ruby runtime) {
+        RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf");
+        RubyClass cRubyOneofDescriptor = protobuf.defineClassUnder("OneofDescriptor", runtime.getObject(), new ObjectAllocator() {
+            @Override
+            public IRubyObject allocate(Ruby ruby, RubyClass rubyClass) {
+                return new RubyOneofDescriptor(ruby, rubyClass);
+            }
+        });
+        cRubyOneofDescriptor.defineAnnotatedMethods(RubyOneofDescriptor.class);
+        cRubyOneofDescriptor.includeModule(runtime.getEnumerable());
+    }
+
+    public RubyOneofDescriptor(Ruby ruby, RubyClass rubyClass) {
+        super(ruby, rubyClass);
+    }
+
+    @JRubyMethod
+    public IRubyObject initialize(ThreadContext context) {
+        builder = DescriptorProtos.OneofDescriptorProto.newBuilder();
+        fields = new ArrayList<RubyFieldDescriptor>();
+        return this;
+    }
+
+    /*
+     * call-seq:
+     *     OneofDescriptor.name => name
+     *
+     * Returns the name of this oneof.
+     */
+    @JRubyMethod(name = "name")
+    public IRubyObject getName(ThreadContext context) {
+        return name;
+    }
+
+    /*
+     * call-seq:
+     *     OneofDescriptor.name = name
+     *
+     * Sets a new name for this oneof. The oneof must not have been added to a
+     * message descriptor yet.
+     */
+    @JRubyMethod(name = "name=")
+    public IRubyObject setName(ThreadContext context, IRubyObject name) {
+        this.name = context.runtime.newString(name.asJavaString());
+        this.builder.setName(name.asJavaString());
+        return context.runtime.getNil();
+    }
+
+    /*
+     * call-seq:
+     *     OneofDescriptor.add_field(field) => nil
+     *
+     * Adds a field to this oneof. The field may have been added to this oneof in
+     * the past, or the message to which this oneof belongs (if any), but may not
+     * have already been added to any other oneof or message. Otherwise, an
+     * exception is raised.
+     *
+     * All fields added to the oneof via this method will be automatically added to
+     * the message to which this oneof belongs, if it belongs to one currently, or
+     * else will be added to any message to which the oneof is later added at the
+     * time that it is added.
+     */
+    @JRubyMethod(name = "add_field")
+    public IRubyObject addField(ThreadContext context, IRubyObject obj) {
+        RubyFieldDescriptor fieldDescriptor = (RubyFieldDescriptor) obj;
+        fieldDescriptor.setOneofName(this.name);
+        fields.add(fieldDescriptor);
+        return context.runtime.getNil();
+    }
+
+    /*
+     * call-seq:
+     *     OneofDescriptor.each(&block) => nil
+     *
+     * Iterates through fields in this oneof, yielding to the block on each one.
+     */
+    @JRubyMethod
+    public IRubyObject each(ThreadContext context, Block block) {
+        for (RubyFieldDescriptor field : fields) {
+            block.yieldSpecific(context, field);
+        }
+        return context.runtime.getNil();
+    }
+
+    public DescriptorProtos.OneofDescriptorProto build(int index) {
+        for (RubyFieldDescriptor field: fields) {
+            field.setOneofIndex(index);
+        }
+        return this.builder.build();
+    }
+
+    protected Collection<RubyFieldDescriptor> getFields() {
+        return fields;
+    }
+
+    protected Descriptors.OneofDescriptor getOneofDescriptor() {
+        RubyFieldDescriptor fieldDescriptor = fields.get(0);
+        return fieldDescriptor.getFieldDef().getContainingOneof();
+    }
+
+    private IRubyObject name;
+    private DescriptorProtos.OneofDescriptorProto.Builder builder;
+    private List<RubyFieldDescriptor> fields;
+}
diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyProtobuf.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyProtobuf.java
new file mode 100644
index 0000000..cb3fcd4
--- /dev/null
+++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyProtobuf.java
@@ -0,0 +1,118 @@
+/*
+ * Protocol Buffers - Google's data interchange format
+ * Copyright 2014 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.
+ */
+
+package com.google.protobuf.jruby;
+
+import org.jruby.Ruby;
+import org.jruby.RubyModule;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.anno.JRubyModule;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+
+@JRubyModule(name = "Protobuf")
+public class RubyProtobuf {
+
+    public static void createProtobuf(Ruby runtime) {
+        RubyModule mGoogle = runtime.getModule("Google");
+        RubyModule mProtobuf = mGoogle.defineModuleUnder("Protobuf");
+        mProtobuf.defineAnnotatedMethods(RubyProtobuf.class);
+    }
+
+    /*
+     * call-seq:
+     *     Google::Protobuf.encode(msg) => bytes
+     *
+     * Encodes the given message object to protocol buffers wire format. This is an
+     * alternative to the #encode method on msg's class.
+     */
+    @JRubyMethod(meta = true)
+    public static IRubyObject encode(ThreadContext context, IRubyObject self, IRubyObject message) {
+        return RubyMessage.encode(context, message.getMetaClass(), message);
+    }
+
+    /*
+     * call-seq:
+     *     Google::Protobuf.decode(class, bytes) => msg
+     *
+     * Decodes the given bytes as protocol buffers wire format under the
+     * interpretation given by the given class's message definition. This is an
+     * alternative to the #decode method on the given class.
+     */
+    @JRubyMethod(meta = true)
+    public static IRubyObject decode(ThreadContext context, IRubyObject self, IRubyObject klazz, IRubyObject message) {
+        return RubyMessage.decode(context, klazz, message);
+    }
+
+    /*
+     * call-seq:
+     *     Google::Protobuf.encode_json(msg) => json_string
+     *
+     * Encodes the given message object to its JSON representation. This is an
+     * alternative to the #encode_json method on msg's class.
+     */
+    @JRubyMethod(name = "encode_json", meta = true)
+    public static IRubyObject encodeJson(ThreadContext context, IRubyObject self, IRubyObject message) {
+        return RubyMessage.encodeJson(context, message.getMetaClass(), message);
+    }
+
+    /*
+     * call-seq:
+     *     Google::Protobuf.decode_json(class, json_string) => msg
+     *
+     * Decodes the given JSON string under the interpretation given by the given
+     * class's message definition. This is an alternative to the #decode_json method
+     * on the given class.
+     */
+    @JRubyMethod(name = "decode_json", meta = true)
+    public static IRubyObject decodeJson(ThreadContext context, IRubyObject self, IRubyObject klazz, IRubyObject message) {
+        return RubyMessage.decodeJson(context, klazz, message);
+    }
+
+    /*
+     * call-seq:
+     *     Google::Protobuf.deep_copy(obj) => copy_of_obj
+     *
+     * Performs a deep copy of either a RepeatedField instance or a message object,
+     * recursively copying its members.
+     */
+    @JRubyMethod(name = "deep_copy", meta = true)
+    public static IRubyObject deepCopy(ThreadContext context, IRubyObject self, IRubyObject message) {
+        if (message instanceof RubyMessage) {
+            return ((RubyMessage) message).deepCopy(context);
+        } else if (message instanceof RubyRepeatedField) {
+            return ((RubyRepeatedField) message).deepCopy(context);
+        } else {
+            return ((RubyMap) message).deepCopy(context);
+        }
+    }
+}
diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyRepeatedField.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyRepeatedField.java
new file mode 100644
index 0000000..9788317
--- /dev/null
+++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyRepeatedField.java
@@ -0,0 +1,391 @@
+/*
+ * Protocol Buffers - Google's data interchange format
+ * Copyright 2014 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.
+ */
+
+package com.google.protobuf.jruby;
+
+import com.google.protobuf.Descriptors;
+import org.jruby.*;
+import org.jruby.anno.JRubyClass;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.Block;
+import org.jruby.runtime.ObjectAllocator;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+
+@JRubyClass(name = "RepeatedClass", include = "Enumerable")
+public class RubyRepeatedField extends RubyObject {
+    public static void createRubyRepeatedField(Ruby runtime) {
+        RubyModule mProtobuf = runtime.getClassFromPath("Google::Protobuf");
+        RubyClass cRepeatedField = mProtobuf.defineClassUnder("RepeatedField", runtime.getObject(),
+                new ObjectAllocator() {
+                    @Override
+                    public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+                        return new RubyRepeatedField(runtime, klazz);
+                    }
+                });
+        cRepeatedField.defineAnnotatedMethods(RubyRepeatedField.class);
+        cRepeatedField.includeModule(runtime.getEnumerable());
+    }
+
+    public RubyRepeatedField(Ruby runtime, RubyClass klazz) {
+        super(runtime, klazz);
+    }
+
+    public RubyRepeatedField(Ruby runtime, RubyClass klazz, Descriptors.FieldDescriptor.Type fieldType, IRubyObject typeClass) {
+        this(runtime, klazz);
+        this.fieldType = fieldType;
+        this.storage = runtime.newArray();
+        this.typeClass = typeClass;
+    }
+
+    @JRubyMethod(required = 1, optional = 2)
+    public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
+        Ruby runtime = context.runtime;
+        this.storage = runtime.newArray();
+        IRubyObject ary = null;
+        if (!(args[0] instanceof RubySymbol)) {
+            throw runtime.newArgumentError("Expected Symbol for type name");
+        }
+        this.fieldType = Utils.rubyToFieldType(args[0]);
+        if (fieldType == Descriptors.FieldDescriptor.Type.MESSAGE
+                || fieldType == Descriptors.FieldDescriptor.Type.ENUM) {
+            if (args.length < 2)
+                throw runtime.newArgumentError("Expected at least 2 arguments for message/enum");
+            typeClass = args[1];
+            if (args.length > 2)
+                ary = args[2];
+            Utils.validateTypeClass(context, fieldType, typeClass);
+        } else {
+            if (args.length > 2)
+                throw runtime.newArgumentError("Too many arguments: expected 1 or 2");
+            if (args.length > 1)
+                ary = args[1];
+        }
+        if (ary != null) {
+            RubyArray arr = ary.convertToArray();
+            for (int i = 0; i < arr.size(); i++) {
+                this.storage.add(arr.eltInternal(i));
+            }
+        }
+        return this;
+    }
+
+    /*
+     * call-seq:
+     *     RepeatedField.[]=(index, value)
+     *
+     * Sets the element at the given index. On out-of-bounds assignments, extends
+     * the array and fills the hole (if any) with default values.
+     */
+    @JRubyMethod(name = "[]=")
+    public IRubyObject indexSet(ThreadContext context, IRubyObject index, IRubyObject value) {
+        Utils.checkType(context, fieldType, value, (RubyModule) typeClass);
+        this.storage.set(RubyNumeric.num2int(index), value);
+        return context.runtime.getNil();
+    }
+
+    /*
+     * call-seq:
+     *     RepeatedField.[](index) => value
+     *
+     * Accesses the element at the given index. Throws an exception on out-of-bounds
+     * errors.
+     */
+    @JRubyMethod(name = "[]")
+    public IRubyObject index(ThreadContext context, IRubyObject index) {
+        return this.storage.eltInternal(RubyNumeric.num2int(index));
+    }
+
+    /*
+     * call-seq:
+     *     RepeatedField.insert(*args)
+     *
+     * Pushes each arg in turn onto the end of the repeated field.
+     */
+    @JRubyMethod(rest = true)
+    public IRubyObject insert(ThreadContext context, IRubyObject[] args) {
+        for (int i = 0; i < args.length; i++) {
+            Utils.checkType(context, fieldType, args[i], (RubyModule) typeClass);
+            this.storage.add(args[i]);
+        }
+        return context.runtime.getNil();
+    }
+
+    /*
+     * call-seq:
+     *     RepeatedField.push(value)
+     *
+     * Adds a new element to the repeated field.
+     */
+    @JRubyMethod(name = {"push", "<<"})
+    public IRubyObject push(ThreadContext context, IRubyObject value) {
+        Utils.checkType(context, fieldType, value, (RubyModule) typeClass);
+        this.storage.add(value);
+        return this;
+    }
+
+    /*
+     * call-seq:
+     *     RepeatedField.pop => value
+     *
+     * Removes the last element and returns it. Throws an exception if the repeated
+     * field is empty.
+     */
+    @JRubyMethod
+    public IRubyObject pop(ThreadContext context) {
+        IRubyObject ret = this.storage.last();
+        this.storage.remove(ret);
+        return ret;
+    }
+
+    /*
+     * call-seq:
+     *     RepeatedField.replace(list)
+     *
+     * Replaces the contents of the repeated field with the given list of elements.
+     */
+    @JRubyMethod
+    public IRubyObject replace(ThreadContext context, IRubyObject list) {
+        RubyArray arr = (RubyArray) list;
+        checkArrayElementType(context, arr);
+        this.storage = arr;
+        return context.runtime.getNil();
+    }
+
+    /*
+     * call-seq:
+     *     RepeatedField.clear
+     *
+     * Clears (removes all elements from) this repeated field.
+     */
+    @JRubyMethod
+    public IRubyObject clear(ThreadContext context) {
+        this.storage.clear();
+        return context.runtime.getNil();
+    }
+
+    /*
+     * call-seq:
+     *     RepeatedField.length
+     *
+     * Returns the length of this repeated field.
+     */
+    @JRubyMethod(name = {"count", "length"})
+    public IRubyObject length(ThreadContext context) {
+        return context.runtime.newFixnum(this.storage.size());
+    }
+
+    /*
+     * call-seq:
+     *     RepeatedField.+(other) => repeated field
+     *
+     * Returns a new repeated field that contains the concatenated list of this
+     * repeated field's elements and other's elements. The other (second) list may
+     * be either another repeated field or a Ruby array.
+     */
+    @JRubyMethod(name = "+")
+    public IRubyObject plus(ThreadContext context, IRubyObject list) {
+        RubyRepeatedField dup = (RubyRepeatedField) dup(context);
+        if (list instanceof RubyArray) {
+            checkArrayElementType(context, (RubyArray) list);
+            dup.storage.addAll((RubyArray) list);
+        } else {
+            RubyRepeatedField repeatedField = (RubyRepeatedField) list;
+            if (! fieldType.equals(repeatedField.fieldType) || (typeClass != null && !
+                    typeClass.equals(repeatedField.typeClass)))
+                throw context.runtime.newArgumentError("Attempt to append RepeatedField with different element type.");
+            dup.storage.addAll((RubyArray) repeatedField.toArray(context));
+        }
+        return dup;
+    }
+
+    /*
+     * call-seq:
+     *     RepeatedField.hash => hash_value
+     *
+     * Returns a hash value computed from this repeated field's elements.
+     */
+    @JRubyMethod
+    public IRubyObject hash(ThreadContext context) {
+        int hashCode = System.identityHashCode(this.storage);
+        return context.runtime.newFixnum(hashCode);
+    }
+
+    /*
+     * call-seq:
+     *     RepeatedField.==(other) => boolean
+     *
+     * Compares this repeated field to another. Repeated fields are equal if their
+     * element types are equal, their lengths are equal, and each element is equal.
+     * Elements are compared as per normal Ruby semantics, by calling their :==
+     * methods (or performing a more efficient comparison for primitive types).
+     */
+    @JRubyMethod(name = "==")
+    public IRubyObject eq(ThreadContext context, IRubyObject other) {
+        return this.toArray(context).op_equal(context, other);
+    }
+
+    /*
+     * call-seq:
+     *     RepeatedField.each(&block)
+     *
+     * Invokes the block once for each element of the repeated field. RepeatedField
+     * also includes Enumerable; combined with this method, the repeated field thus
+     * acts like an ordinary Ruby sequence.
+     */
+    @JRubyMethod
+    public IRubyObject each(ThreadContext context, Block block) {
+        this.storage.each(context, block);
+        return context.runtime.getNil();
+    }
+
+    @JRubyMethod(name = {"to_ary", "to_a"})
+    public IRubyObject toArray(ThreadContext context) {
+        for (int i = 0; i < this.storage.size(); i++) {
+            IRubyObject defaultValue = defaultValue(context);
+            if (storage.eltInternal(i).isNil()) {
+                storage.set(i, defaultValue);
+            }
+        }
+        return this.storage;
+    }
+
+    /*
+     * call-seq:
+     *     RepeatedField.dup => repeated_field
+     *
+     * Duplicates this repeated field with a shallow copy. References to all
+     * non-primitive element objects (e.g., submessages) are shared.
+     */
+    @JRubyMethod
+    public IRubyObject dup(ThreadContext context) {
+        RubyRepeatedField dup = new RubyRepeatedField(context.runtime, metaClass, fieldType, typeClass);
+        for (int i = 0; i < this.storage.size(); i++) {
+            dup.push(context, this.storage.eltInternal(i));
+        }
+        return dup;
+    }
+
+    /*
+     * call-seq:
+     *     RepeatedField.inspect => string
+     *
+     * Returns a string representing this repeated field's elements. It will be
+     * formated as "[<element>, <element>, ...]", with each element's string
+     * representation computed by its own #inspect method.
+     */
+    @JRubyMethod
+    public IRubyObject inspect() {
+        StringBuilder str = new StringBuilder("[");
+        for (int i = 0; i < this.storage.size(); i++) {
+            str.append(storage.eltInternal(i).inspect());
+            str.append(", ");
+        }
+
+        if (str.length() > 1) {
+            str.replace(str.length() - 2, str.length(), "]");
+        } else {
+            str.append("]");
+        }
+
+        return getRuntime().newString(str.toString());
+    }
+
+    // Java API
+    protected IRubyObject get(int index) {
+        return this.storage.eltInternal(index);
+    }
+
+    protected RubyRepeatedField deepCopy(ThreadContext context) {
+        RubyRepeatedField copy = new RubyRepeatedField(context.runtime, metaClass, fieldType, typeClass);
+        for (int i = 0; i < size(); i++) {
+            IRubyObject value = storage.eltInternal(i);
+            if (fieldType == Descriptors.FieldDescriptor.Type.MESSAGE) {
+                copy.storage.add(((RubyMessage) value).deepCopy(context));
+            } else {
+                copy.storage.add(value);
+            }
+        }
+        return copy;
+    }
+
+    protected int size() {
+        return this.storage.size();
+    }
+
+    private IRubyObject defaultValue(ThreadContext context) {
+        SentinelOuterClass.Sentinel sentinel = SentinelOuterClass.Sentinel.getDefaultInstance();
+        Object value;
+        switch (fieldType) {
+            case INT32:
+                value = sentinel.getDefaultInt32();
+                break;
+            case INT64:
+                value = sentinel.getDefaultInt64();
+                break;
+            case UINT32:
+                value = sentinel.getDefaultUnit32();
+                break;
+            case UINT64:
+                value = sentinel.getDefaultUint64();
+                break;
+            case FLOAT:
+                value = sentinel.getDefaultFloat();
+                break;
+            case DOUBLE:
+                value = sentinel.getDefaultDouble();
+                break;
+            case BOOL:
+                value = sentinel.getDefaultBool();
+                break;
+            case BYTES:
+                value = sentinel.getDefaultBytes();
+                break;
+            case STRING:
+                value = sentinel.getDefaultString();
+                break;
+            default:
+                return context.runtime.getNil();
+        }
+        return Utils.wrapPrimaryValue(context, fieldType, value);
+    }
+
+    private void checkArrayElementType(ThreadContext context, RubyArray arr) {
+        for (int i = 0; i < arr.getLength(); i++) {
+            Utils.checkType(context, fieldType, arr.eltInternal(i), (RubyModule) typeClass);
+        }
+    }
+
+    private RubyArray storage;
+    private Descriptors.FieldDescriptor.Type fieldType;
+    private IRubyObject typeClass;
+}
diff --git a/ruby/src/main/java/com/google/protobuf/jruby/SentinelOuterClass.java b/ruby/src/main/java/com/google/protobuf/jruby/SentinelOuterClass.java
new file mode 100644
index 0000000..54f2c72
--- /dev/null
+++ b/ruby/src/main/java/com/google/protobuf/jruby/SentinelOuterClass.java
@@ -0,0 +1,776 @@
+/*
+ * Protocol Buffers - Google's data interchange format
+ * Copyright 2014 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.
+ */
+
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: sentinel.proto
+
+package com.google.protobuf.jruby;
+
+public final class SentinelOuterClass {
+  private SentinelOuterClass() {}
+  public static void registerAllExtensions(
+      com.google.protobuf.ExtensionRegistry registry) {
+  }
+  public interface SentinelOrBuilder extends
+      // @@protoc_insertion_point(interface_extends:com.google.protobuf.jruby.Sentinel)
+      com.google.protobuf.MessageOrBuilder {
+
+    /**
+     * <code>optional int32 default_int32 = 1;</code>
+     */
+    int getDefaultInt32();
+
+    /**
+     * <code>optional int64 default_int64 = 2;</code>
+     */
+    long getDefaultInt64();
+
+    /**
+     * <code>optional uint32 default_unit32 = 3;</code>
+     */
+    int getDefaultUnit32();
+
+    /**
+     * <code>optional uint64 default_uint64 = 4;</code>
+     */
+    long getDefaultUint64();
+
+    /**
+     * <code>optional string default_string = 5;</code>
+     */
+    java.lang.String getDefaultString();
+    /**
+     * <code>optional string default_string = 5;</code>
+     */
+    com.google.protobuf.ByteString
+        getDefaultStringBytes();
+
+    /**
+     * <code>optional bool default_bool = 6;</code>
+     */
+    boolean getDefaultBool();
+
+    /**
+     * <code>optional float default_float = 7;</code>
+     */
+    float getDefaultFloat();
+
+    /**
+     * <code>optional double default_double = 8;</code>
+     */
+    double getDefaultDouble();
+
+    /**
+     * <code>optional bytes default_bytes = 9;</code>
+     */
+    com.google.protobuf.ByteString getDefaultBytes();
+  }
+  /**
+   * Protobuf type {@code com.google.protobuf.jruby.Sentinel}
+   */
+  public  static final class Sentinel extends
+      com.google.protobuf.GeneratedMessage implements
+      // @@protoc_insertion_point(message_implements:com.google.protobuf.jruby.Sentinel)
+      SentinelOrBuilder {
+    // Use Sentinel.newBuilder() to construct.
+    private Sentinel(com.google.protobuf.GeneratedMessage.Builder builder) {
+      super(builder);
+    }
+    private Sentinel() {
+      defaultInt32_ = 0;
+      defaultInt64_ = 0L;
+      defaultUnit32_ = 0;
+      defaultUint64_ = 0L;
+      defaultString_ = "";
+      defaultBool_ = false;
+      defaultFloat_ = 0F;
+      defaultDouble_ = 0D;
+      defaultBytes_ = com.google.protobuf.ByteString.EMPTY;
+    }
+
+    @java.lang.Override
+    public final com.google.protobuf.UnknownFieldSet
+    getUnknownFields() {
+      return com.google.protobuf.UnknownFieldSet.getDefaultInstance();
+    }
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return com.google.protobuf.jruby.SentinelOuterClass.internal_static_com_google_protobuf_jruby_Sentinel_descriptor;
+    }
+
+    protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return com.google.protobuf.jruby.SentinelOuterClass.internal_static_com_google_protobuf_jruby_Sentinel_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              com.google.protobuf.jruby.SentinelOuterClass.Sentinel.class, com.google.protobuf.jruby.SentinelOuterClass.Sentinel.Builder.class);
+    }
+
+    public static final com.google.protobuf.Parser<Sentinel> PARSER =
+        new com.google.protobuf.AbstractParser<Sentinel>() {
+      public Sentinel parsePartialFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        Builder builder = newBuilder();
+        try {
+          builder.mergeFrom(input, extensionRegistry);
+        } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+          throw e.setUnfinishedMessage(builder.buildPartial());
+        } catch (java.io.IOException e) {
+          throw new com.google.protobuf.InvalidProtocolBufferException(
+              e.getMessage()).setUnfinishedMessage(builder.buildPartial());
+        }
+        return builder.buildPartial();
+      }
+    };
+
+    @java.lang.Override
+    public com.google.protobuf.Parser<Sentinel> getParserForType() {
+      return PARSER;
+    }
+
+    public static final int DEFAULT_INT32_FIELD_NUMBER = 1;
+    private int defaultInt32_;
+    /**
+     * <code>optional int32 default_int32 = 1;</code>
+     */
+    public int getDefaultInt32() {
+      return defaultInt32_;
+    }
+
+    public static final int DEFAULT_INT64_FIELD_NUMBER = 2;
+    private long defaultInt64_;
+    /**
+     * <code>optional int64 default_int64 = 2;</code>
+     */
+    public long getDefaultInt64() {
+      return defaultInt64_;
+    }
+
+    public static final int DEFAULT_UNIT32_FIELD_NUMBER = 3;
+    private int defaultUnit32_;
+    /**
+     * <code>optional uint32 default_unit32 = 3;</code>
+     */
+    public int getDefaultUnit32() {
+      return defaultUnit32_;
+    }
+
+    public static final int DEFAULT_UINT64_FIELD_NUMBER = 4;
+    private long defaultUint64_;
+    /**
+     * <code>optional uint64 default_uint64 = 4;</code>
+     */
+    public long getDefaultUint64() {
+      return defaultUint64_;
+    }
+
+    public static final int DEFAULT_STRING_FIELD_NUMBER = 5;
+    private java.lang.Object defaultString_;
+    /**
+     * <code>optional string default_string = 5;</code>
+     */
+    public java.lang.String getDefaultString() {
+      java.lang.Object ref = defaultString_;
+      if (ref instanceof java.lang.String) {
+        return (java.lang.String) ref;
+      } else {
+        com.google.protobuf.ByteString bs = 
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        if (bs.isValidUtf8()) {
+          defaultString_ = s;
+        }
+        return s;
+      }
+    }
+    /**
+     * <code>optional string default_string = 5;</code>
+     */
+    public com.google.protobuf.ByteString
+        getDefaultStringBytes() {
+      java.lang.Object ref = defaultString_;
+      if (ref instanceof java.lang.String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        defaultString_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
+    }
+
+    public static final int DEFAULT_BOOL_FIELD_NUMBER = 6;
+    private boolean defaultBool_;
+    /**
+     * <code>optional bool default_bool = 6;</code>
+     */
+    public boolean getDefaultBool() {
+      return defaultBool_;
+    }
+
+    public static final int DEFAULT_FLOAT_FIELD_NUMBER = 7;
+    private float defaultFloat_;
+    /**
+     * <code>optional float default_float = 7;</code>
+     */
+    public float getDefaultFloat() {
+      return defaultFloat_;
+    }
+
+    public static final int DEFAULT_DOUBLE_FIELD_NUMBER = 8;
+    private double defaultDouble_;
+    /**
+     * <code>optional double default_double = 8;</code>
+     */
+    public double getDefaultDouble() {
+      return defaultDouble_;
+    }
+
+    public static final int DEFAULT_BYTES_FIELD_NUMBER = 9;
+    private com.google.protobuf.ByteString defaultBytes_;
+    /**
+     * <code>optional bytes default_bytes = 9;</code>
+     */
+    public com.google.protobuf.ByteString getDefaultBytes() {
+      return defaultBytes_;
+    }
+
+    public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseFrom(
+        com.google.protobuf.ByteString data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseFrom(
+        com.google.protobuf.ByteString data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseFrom(byte[] data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseFrom(
+        byte[] data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input);
+    }
+    public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input, extensionRegistry);
+    }
+    public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseDelimitedFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return PARSER.parseDelimitedFrom(input);
+    }
+    public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseDelimitedFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return PARSER.parseDelimitedFrom(input, extensionRegistry);
+    }
+    public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseFrom(
+        com.google.protobuf.CodedInputStream input)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input);
+    }
+    public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input, extensionRegistry);
+    }
+
+    public static Builder newBuilder() { return new Builder(); }
+    public Builder newBuilderForType() { return newBuilder(); }
+    public static Builder newBuilder(com.google.protobuf.jruby.SentinelOuterClass.Sentinel prototype) {
+      return newBuilder().mergeFrom(prototype);
+    }
+    public Builder toBuilder() { return newBuilder(this); }
+
+    @java.lang.Override
+    protected Builder newBuilderForType(
+        com.google.protobuf.GeneratedMessage.BuilderParent parent) {
+      Builder builder = new Builder(parent);
+      return builder;
+    }
+    /**
+     * Protobuf type {@code com.google.protobuf.jruby.Sentinel}
+     */
+    public static final class Builder extends
+        com.google.protobuf.GeneratedMessage.Builder<Builder> implements
+        // @@protoc_insertion_point(builder_implements:com.google.protobuf.jruby.Sentinel)
+        com.google.protobuf.jruby.SentinelOuterClass.SentinelOrBuilder {
+      public static final com.google.protobuf.Descriptors.Descriptor
+          getDescriptor() {
+        return com.google.protobuf.jruby.SentinelOuterClass.internal_static_com_google_protobuf_jruby_Sentinel_descriptor;
+      }
+
+      protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+          internalGetFieldAccessorTable() {
+        return com.google.protobuf.jruby.SentinelOuterClass.internal_static_com_google_protobuf_jruby_Sentinel_fieldAccessorTable
+            .ensureFieldAccessorsInitialized(
+                com.google.protobuf.jruby.SentinelOuterClass.Sentinel.class, com.google.protobuf.jruby.SentinelOuterClass.Sentinel.Builder.class);
+      }
+
+      // Construct using com.google.protobuf.jruby.SentinelOuterClass.Sentinel.newBuilder()
+      private Builder() {
+        maybeForceBuilderInitialization();
+      }
+
+      private Builder(
+          com.google.protobuf.GeneratedMessage.BuilderParent parent) {
+        super(parent);
+        maybeForceBuilderInitialization();
+      }
+      private void maybeForceBuilderInitialization() {
+        if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {
+        }
+      }
+      public Builder clear() {
+        super.clear();
+        defaultInt32_ = 0;
+
+        defaultInt64_ = 0L;
+
+        defaultUnit32_ = 0;
+
+        defaultUint64_ = 0L;
+
+        defaultString_ = "";
+
+        defaultBool_ = false;
+
+        defaultFloat_ = 0F;
+
+        defaultDouble_ = 0D;
+
+        defaultBytes_ = com.google.protobuf.ByteString.EMPTY;
+
+        return this;
+      }
+
+      public com.google.protobuf.Descriptors.Descriptor
+          getDescriptorForType() {
+        return com.google.protobuf.jruby.SentinelOuterClass.internal_static_com_google_protobuf_jruby_Sentinel_descriptor;
+      }
+
+      public com.google.protobuf.jruby.SentinelOuterClass.Sentinel getDefaultInstanceForType() {
+        return com.google.protobuf.jruby.SentinelOuterClass.Sentinel.getDefaultInstance();
+      }
+
+      public com.google.protobuf.jruby.SentinelOuterClass.Sentinel build() {
+        com.google.protobuf.jruby.SentinelOuterClass.Sentinel result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(result);
+        }
+        return result;
+      }
+
+      public com.google.protobuf.jruby.SentinelOuterClass.Sentinel buildPartial() {
+        com.google.protobuf.jruby.SentinelOuterClass.Sentinel result = new com.google.protobuf.jruby.SentinelOuterClass.Sentinel(this);
+        result.defaultInt32_ = defaultInt32_;
+        result.defaultInt64_ = defaultInt64_;
+        result.defaultUnit32_ = defaultUnit32_;
+        result.defaultUint64_ = defaultUint64_;
+        result.defaultString_ = defaultString_;
+        result.defaultBool_ = defaultBool_;
+        result.defaultFloat_ = defaultFloat_;
+        result.defaultDouble_ = defaultDouble_;
+        result.defaultBytes_ = defaultBytes_;
+        onBuilt();
+        return result;
+      }
+
+
+      private int defaultInt32_ ;
+      /**
+       * <code>optional int32 default_int32 = 1;</code>
+       */
+      public int getDefaultInt32() {
+        return defaultInt32_;
+      }
+      /**
+       * <code>optional int32 default_int32 = 1;</code>
+       */
+      public Builder setDefaultInt32(int value) {
+        
+        defaultInt32_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>optional int32 default_int32 = 1;</code>
+       */
+      public Builder clearDefaultInt32() {
+        
+        defaultInt32_ = 0;
+        onChanged();
+        return this;
+      }
+
+      private long defaultInt64_ ;
+      /**
+       * <code>optional int64 default_int64 = 2;</code>
+       */
+      public long getDefaultInt64() {
+        return defaultInt64_;
+      }
+      /**
+       * <code>optional int64 default_int64 = 2;</code>
+       */
+      public Builder setDefaultInt64(long value) {
+        
+        defaultInt64_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>optional int64 default_int64 = 2;</code>
+       */
+      public Builder clearDefaultInt64() {
+        
+        defaultInt64_ = 0L;
+        onChanged();
+        return this;
+      }
+
+      private int defaultUnit32_ ;
+      /**
+       * <code>optional uint32 default_unit32 = 3;</code>
+       */
+      public int getDefaultUnit32() {
+        return defaultUnit32_;
+      }
+      /**
+       * <code>optional uint32 default_unit32 = 3;</code>
+       */
+      public Builder setDefaultUnit32(int value) {
+        
+        defaultUnit32_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>optional uint32 default_unit32 = 3;</code>
+       */
+      public Builder clearDefaultUnit32() {
+        
+        defaultUnit32_ = 0;
+        onChanged();
+        return this;
+      }
+
+      private long defaultUint64_ ;
+      /**
+       * <code>optional uint64 default_uint64 = 4;</code>
+       */
+      public long getDefaultUint64() {
+        return defaultUint64_;
+      }
+      /**
+       * <code>optional uint64 default_uint64 = 4;</code>
+       */
+      public Builder setDefaultUint64(long value) {
+        
+        defaultUint64_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>optional uint64 default_uint64 = 4;</code>
+       */
+      public Builder clearDefaultUint64() {
+        
+        defaultUint64_ = 0L;
+        onChanged();
+        return this;
+      }
+
+      private java.lang.Object defaultString_ = "";
+      /**
+       * <code>optional string default_string = 5;</code>
+       */
+      public java.lang.String getDefaultString() {
+        java.lang.Object ref = defaultString_;
+        if (!(ref instanceof java.lang.String)) {
+          com.google.protobuf.ByteString bs =
+              (com.google.protobuf.ByteString) ref;
+          java.lang.String s = bs.toStringUtf8();
+          if (bs.isValidUtf8()) {
+            defaultString_ = s;
+          }
+          return s;
+        } else {
+          return (java.lang.String) ref;
+        }
+      }
+      /**
+       * <code>optional string default_string = 5;</code>
+       */
+      public com.google.protobuf.ByteString
+          getDefaultStringBytes() {
+        java.lang.Object ref = defaultString_;
+        if (ref instanceof String) {
+          com.google.protobuf.ByteString b = 
+              com.google.protobuf.ByteString.copyFromUtf8(
+                  (java.lang.String) ref);
+          defaultString_ = b;
+          return b;
+        } else {
+          return (com.google.protobuf.ByteString) ref;
+        }
+      }
+      /**
+       * <code>optional string default_string = 5;</code>
+       */
+      public Builder setDefaultString(
+          java.lang.String value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  
+        defaultString_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>optional string default_string = 5;</code>
+       */
+      public Builder clearDefaultString() {
+        
+        defaultString_ = getDefaultInstance().getDefaultString();
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>optional string default_string = 5;</code>
+       */
+      public Builder setDefaultStringBytes(
+          com.google.protobuf.ByteString value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  
+        defaultString_ = value;
+        onChanged();
+        return this;
+      }
+
+      private boolean defaultBool_ ;
+      /**
+       * <code>optional bool default_bool = 6;</code>
+       */
+      public boolean getDefaultBool() {
+        return defaultBool_;
+      }
+      /**
+       * <code>optional bool default_bool = 6;</code>
+       */
+      public Builder setDefaultBool(boolean value) {
+        
+        defaultBool_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>optional bool default_bool = 6;</code>
+       */
+      public Builder clearDefaultBool() {
+        
+        defaultBool_ = false;
+        onChanged();
+        return this;
+      }
+
+      private float defaultFloat_ ;
+      /**
+       * <code>optional float default_float = 7;</code>
+       */
+      public float getDefaultFloat() {
+        return defaultFloat_;
+      }
+      /**
+       * <code>optional float default_float = 7;</code>
+       */
+      public Builder setDefaultFloat(float value) {
+        
+        defaultFloat_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>optional float default_float = 7;</code>
+       */
+      public Builder clearDefaultFloat() {
+        
+        defaultFloat_ = 0F;
+        onChanged();
+        return this;
+      }
+
+      private double defaultDouble_ ;
+      /**
+       * <code>optional double default_double = 8;</code>
+       */
+      public double getDefaultDouble() {
+        return defaultDouble_;
+      }
+      /**
+       * <code>optional double default_double = 8;</code>
+       */
+      public Builder setDefaultDouble(double value) {
+        
+        defaultDouble_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>optional double default_double = 8;</code>
+       */
+      public Builder clearDefaultDouble() {
+        
+        defaultDouble_ = 0D;
+        onChanged();
+        return this;
+      }
+
+      private com.google.protobuf.ByteString defaultBytes_ = com.google.protobuf.ByteString.EMPTY;
+      /**
+       * <code>optional bytes default_bytes = 9;</code>
+       */
+      public com.google.protobuf.ByteString getDefaultBytes() {
+        return defaultBytes_;
+      }
+      /**
+       * <code>optional bytes default_bytes = 9;</code>
+       */
+      public Builder setDefaultBytes(com.google.protobuf.ByteString value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  
+        defaultBytes_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>optional bytes default_bytes = 9;</code>
+       */
+      public Builder clearDefaultBytes() {
+        
+        defaultBytes_ = getDefaultInstance().getDefaultBytes();
+        onChanged();
+        return this;
+      }
+      public final Builder setUnknownFields(
+          final com.google.protobuf.UnknownFieldSet unknownFields) {
+        return this;
+      }
+
+      public final Builder mergeUnknownFields(
+          final com.google.protobuf.UnknownFieldSet unknownFields) {
+        return this;
+      }
+
+
+      // @@protoc_insertion_point(builder_scope:com.google.protobuf.jruby.Sentinel)
+    }
+
+    // @@protoc_insertion_point(class_scope:com.google.protobuf.jruby.Sentinel)
+    private static final com.google.protobuf.jruby.SentinelOuterClass.Sentinel defaultInstance;static {
+      defaultInstance = new com.google.protobuf.jruby.SentinelOuterClass.Sentinel();
+    }
+
+    public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel getDefaultInstance() {
+      return defaultInstance;
+    }
+
+    public com.google.protobuf.jruby.SentinelOuterClass.Sentinel getDefaultInstanceForType() {
+      return defaultInstance;
+    }
+
+  }
+
+  private static final com.google.protobuf.Descriptors.Descriptor
+    internal_static_com_google_protobuf_jruby_Sentinel_descriptor;
+  private static
+    com.google.protobuf.GeneratedMessage.FieldAccessorTable
+      internal_static_com_google_protobuf_jruby_Sentinel_fieldAccessorTable;
+
+  public static com.google.protobuf.Descriptors.FileDescriptor
+      getDescriptor() {
+    return descriptor;
+  }
+  private static com.google.protobuf.Descriptors.FileDescriptor
+      descriptor;
+  static {
+    java.lang.String[] descriptorData = {
+      "\n\016sentinel.proto\022\031com.google.protobuf.jr" +
+      "uby\"\334\001\n\010Sentinel\022\025\n\rdefault_int32\030\001 \001(\005\022" +
+      "\025\n\rdefault_int64\030\002 \001(\003\022\026\n\016default_unit32" +
+      "\030\003 \001(\r\022\026\n\016default_uint64\030\004 \001(\004\022\026\n\016defaul" +
+      "t_string\030\005 \001(\t\022\024\n\014default_bool\030\006 \001(\010\022\025\n\r" +
+      "default_float\030\007 \001(\002\022\026\n\016default_double\030\010 " +
+      "\001(\001\022\025\n\rdefault_bytes\030\t \001(\014B\002H\002b\006proto3"
+    };
+    com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
+        new com.google.protobuf.Descriptors.FileDescriptor.    InternalDescriptorAssigner() {
+          public com.google.protobuf.ExtensionRegistry assignDescriptors(
+              com.google.protobuf.Descriptors.FileDescriptor root) {
+            descriptor = root;
+            return null;
+          }
+        };
+    com.google.protobuf.Descriptors.FileDescriptor
+      .internalBuildGeneratedFileFrom(descriptorData,
+        new com.google.protobuf.Descriptors.FileDescriptor[] {
+        }, assigner);
+    internal_static_com_google_protobuf_jruby_Sentinel_descriptor =
+      getDescriptor().getMessageTypes().get(0);
+    internal_static_com_google_protobuf_jruby_Sentinel_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessage.FieldAccessorTable(
+        internal_static_com_google_protobuf_jruby_Sentinel_descriptor,
+        new java.lang.String[] { "DefaultInt32", "DefaultInt64", "DefaultUnit32", "DefaultUint64", "DefaultString", "DefaultBool", "DefaultFloat", "DefaultDouble", "DefaultBytes", });
+  }
+
+  // @@protoc_insertion_point(outer_class_scope)
+}
diff --git a/ruby/src/main/java/com/google/protobuf/jruby/Utils.java b/ruby/src/main/java/com/google/protobuf/jruby/Utils.java
new file mode 100644
index 0000000..596a097
--- /dev/null
+++ b/ruby/src/main/java/com/google/protobuf/jruby/Utils.java
@@ -0,0 +1,300 @@
+/*
+ * Protocol Buffers - Google's data interchange format
+ * Copyright 2014 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.
+ */
+
+package com.google.protobuf.jruby;
+
+import com.google.protobuf.ByteString;
+import com.google.protobuf.DescriptorProtos;
+import com.google.protobuf.Descriptors;
+import org.jcodings.Encoding;
+import org.jcodings.specific.ASCIIEncoding;
+import org.jcodings.specific.USASCIIEncoding;
+import org.jcodings.specific.UTF8Encoding;
+import org.jruby.*;
+import org.jruby.runtime.Block;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+
+import java.math.BigInteger;
+
+public class Utils {
+    public static Descriptors.FieldDescriptor.Type rubyToFieldType(IRubyObject typeClass) {
+        return Descriptors.FieldDescriptor.Type.valueOf(typeClass.asJavaString().toUpperCase());
+    }
+
+    public static IRubyObject fieldTypeToRuby(ThreadContext context, Descriptors.FieldDescriptor.Type type) {
+        return fieldTypeToRuby(context, type.name());
+    }
+
+    public static IRubyObject fieldTypeToRuby(ThreadContext context, DescriptorProtos.FieldDescriptorProto.Type type) {
+        return fieldTypeToRuby(context, type.name());
+    }
+
+    private static IRubyObject fieldTypeToRuby(ThreadContext context, String typeName) {
+
+        return context.runtime.newSymbol(typeName.replace("TYPE_", "").toLowerCase());
+    }
+
+    public static void checkType(ThreadContext context, Descriptors.FieldDescriptor.Type fieldType,
+                            IRubyObject value, RubyModule typeClass) {
+        Ruby runtime = context.runtime;
+        Object val;
+        switch(fieldType) {
+            case INT32:
+            case INT64:
+            case UINT32:
+            case UINT64:
+                if (!isRubyNum(value)) {
+                    throw runtime.newTypeError("Expected number type for integral field.");
+                }
+                switch(fieldType) {
+                    case INT32:
+                        RubyNumeric.num2int(value);
+                        break;
+                    case INT64:
+                        RubyNumeric.num2long(value);
+                        break;
+                    case UINT32:
+                        num2uint(value);
+                        break;
+                    default:
+                        num2ulong(context.runtime, value);
+                        break;
+                }
+                checkIntTypePrecision(context, fieldType, value);
+                break;
+            case FLOAT:
+                if (!isRubyNum(value))
+                    throw runtime.newTypeError("Expected number type for float field.");
+                break;
+            case DOUBLE:
+                if (!isRubyNum(value))
+                    throw runtime.newTypeError("Expected number type for double field.");
+                break;
+            case BOOL:
+                if (!(value instanceof RubyBoolean))
+                    throw runtime.newTypeError("Invalid argument for boolean field.");
+                break;
+            case BYTES:
+            case STRING:
+                validateStringEncoding(context.runtime, fieldType, value);
+                break;
+            case MESSAGE:
+                if (value.getMetaClass() != typeClass) {
+                    throw runtime.newTypeError(value, typeClass);
+                }
+                break;
+            case ENUM:
+                if (value instanceof RubySymbol) {
+                    Descriptors.EnumDescriptor enumDescriptor =
+                            ((RubyEnumDescriptor) typeClass.getInstanceVariable(DESCRIPTOR_INSTANCE_VAR)).getDescriptor();
+                    val = enumDescriptor.findValueByName(value.asJavaString());
+                    if (val == null)
+                        throw runtime.newRangeError("Enum value " + value + " is not found.");
+                } else if(!isRubyNum(value)) {
+                    throw runtime.newTypeError("Expected number or symbol type for enum field.");
+                }
+                break;
+            default:
+                break;
+        }
+    }
+
+    public static IRubyObject wrapPrimaryValue(ThreadContext context, Descriptors.FieldDescriptor.Type fieldType, Object value) {
+        Ruby runtime = context.runtime;
+        switch (fieldType) {
+            case INT32:
+                return runtime.newFixnum((Integer) value);
+            case INT64:
+                return runtime.newFixnum((Long) value);
+            case UINT32:
+                return runtime.newFixnum(((Integer) value) & (-1l >>> 32));
+            case UINT64:
+                long ret = (Long) value;
+                return ret >= 0 ? runtime.newFixnum(ret) :
+                        RubyBignum.newBignum(runtime, UINT64_COMPLEMENTARY.add(new BigInteger(ret + "")));
+            case FLOAT:
+                return runtime.newFloat((Float) value);
+            case DOUBLE:
+                return runtime.newFloat((Double) value);
+            case BOOL:
+                return (Boolean) value ? runtime.getTrue() : runtime.getFalse();
+            case BYTES:
+                return runtime.newString(((ByteString) value).toStringUtf8());
+            case STRING:
+                return runtime.newString(value.toString());
+            default:
+                return runtime.getNil();
+        }
+    }
+
+    public static int num2uint(IRubyObject value) {
+        long longVal = RubyNumeric.num2long(value);
+        if (longVal > UINT_MAX)
+            throw value.getRuntime().newRangeError("Integer " + longVal + " too big to convert to 'unsigned int'");
+        long num = longVal;
+        if (num > Integer.MAX_VALUE || num < Integer.MIN_VALUE)
+            // encode to UINT32
+            num = (-longVal ^ (-1l >>> 32) ) + 1;
+        RubyNumeric.checkInt(value, num);
+        return (int) num;
+    }
+
+    public static long num2ulong(Ruby runtime, IRubyObject value) {
+        if (value instanceof RubyFloat) {
+            RubyBignum bignum = RubyBignum.newBignum(runtime, ((RubyFloat) value).getDoubleValue());
+            return RubyBignum.big2ulong(bignum);
+        } else if (value instanceof RubyBignum) {
+            return RubyBignum.big2ulong((RubyBignum) value);
+        } else {
+            return RubyNumeric.num2long(value);
+        }
+    }
+
+    public static void validateStringEncoding(Ruby runtime, Descriptors.FieldDescriptor.Type type, IRubyObject value) {
+        if (!(value instanceof RubyString))
+            throw runtime.newTypeError("Invalid argument for string field.");
+        Encoding encoding = ((RubyString) value).getEncoding();
+        switch(type) {
+            case BYTES:
+                if (encoding != ASCIIEncoding.INSTANCE)
+                    throw runtime.newTypeError("Encoding for bytes fields" +
+                            " must be \"ASCII-8BIT\", but was " + encoding);
+                break;
+            case STRING:
+                if (encoding != UTF8Encoding.INSTANCE
+                        && encoding != USASCIIEncoding.INSTANCE)
+                    throw runtime.newTypeError("Encoding for string fields" +
+                            " must be \"UTF-8\" or \"ASCII\", but was " + encoding);
+                break;
+            default:
+                break;
+        }
+    }
+
+    public static void checkNameAvailability(ThreadContext context, String name) {
+        if (context.runtime.getObject().getConstantAt(name) != null)
+            throw context.runtime.newNameError(name + " is already defined", name);
+    }
+
+    /**
+     * Replace invalid "." in descriptor with __DOT__
+     * @param name
+     * @return
+     */
+    public static String escapeIdentifier(String name) {
+        return name.replace(".", BADNAME_REPLACEMENT);
+    }
+
+    /**
+     * Replace __DOT__ in descriptor name with "."
+     * @param name
+     * @return
+     */
+    public static String unescapeIdentifier(String name) {
+        return name.replace(BADNAME_REPLACEMENT, ".");
+    }
+
+    public static boolean isMapEntry(Descriptors.FieldDescriptor fieldDescriptor) {
+        return fieldDescriptor.getType() == Descriptors.FieldDescriptor.Type.MESSAGE &&
+                fieldDescriptor.isRepeated() &&
+                fieldDescriptor.getMessageType().getOptions().getMapEntry();
+    }
+
+    public static RubyFieldDescriptor msgdefCreateField(ThreadContext context, String label, IRubyObject name,
+                                      IRubyObject type, IRubyObject number, IRubyObject typeClass, RubyClass cFieldDescriptor) {
+        Ruby runtime = context.runtime;
+        RubyFieldDescriptor fieldDef = (RubyFieldDescriptor) cFieldDescriptor.newInstance(context, Block.NULL_BLOCK);
+        fieldDef.setLabel(context, runtime.newString(label));
+        fieldDef.setName(context, name);
+        fieldDef.setType(context, type);
+        fieldDef.setNumber(context, number);
+
+        if (!typeClass.isNil()) {
+            if (!(typeClass instanceof RubyString)) {
+                throw runtime.newArgumentError("expected string for type class");
+            }
+            fieldDef.setSubmsgName(context, typeClass);
+        }
+        return fieldDef;
+    }
+
+    protected static void checkIntTypePrecision(ThreadContext context, Descriptors.FieldDescriptor.Type type, IRubyObject value) {
+        if (value instanceof RubyFloat) {
+            double doubleVal = RubyNumeric.num2dbl(value);
+            if (Math.floor(doubleVal) != doubleVal) {
+                throw context.runtime.newRangeError("Non-integral floating point value assigned to integer field.");
+            }
+        }
+        if (type == Descriptors.FieldDescriptor.Type.UINT32 || type == Descriptors.FieldDescriptor.Type.UINT64) {
+            if (RubyNumeric.num2dbl(value) < 0) {
+                throw context.runtime.newRangeError("Assigning negative value to unsigned integer field.");
+            }
+        }
+    }
+
+    protected static boolean isRubyNum(Object value) {
+        return value instanceof RubyFixnum || value instanceof RubyFloat || value instanceof RubyBignum;
+    }
+
+    protected static void validateTypeClass(ThreadContext context, Descriptors.FieldDescriptor.Type type, IRubyObject value) {
+        Ruby runtime = context.runtime;
+        if (!(value instanceof RubyModule)) {
+            throw runtime.newArgumentError("TypeClass has incorrect type");
+        }
+        RubyModule klass = (RubyModule) value;
+        IRubyObject descriptor = klass.getInstanceVariable(DESCRIPTOR_INSTANCE_VAR);
+        if (descriptor.isNil()) {
+            throw runtime.newArgumentError("Type class has no descriptor. Please pass a " +
+                    "class or enum as returned by the DescriptorPool.");
+        }
+        if (type == Descriptors.FieldDescriptor.Type.MESSAGE) {
+            if (! (descriptor instanceof RubyDescriptor)) {
+                throw runtime.newArgumentError("Descriptor has an incorrect type");
+            }
+        } else if (type == Descriptors.FieldDescriptor.Type.ENUM) {
+            if (! (descriptor instanceof RubyEnumDescriptor)) {
+                throw runtime.newArgumentError("Descriptor has an incorrect type");
+            }
+        }
+    }
+
+    public static String BADNAME_REPLACEMENT = "__DOT__";
+
+    public static String DESCRIPTOR_INSTANCE_VAR = "@descriptor";
+
+    public static String EQUAL_SIGN = "=";
+
+    private static BigInteger UINT64_COMPLEMENTARY = new BigInteger("18446744073709551616"); //Math.pow(2, 64)
+
+    private static long UINT_MAX = 0xffffffffl;
+}
diff --git a/ruby/src/main/java/google/ProtobufJavaService.java b/ruby/src/main/java/google/ProtobufJavaService.java
new file mode 100644
index 0000000..bffb492
--- /dev/null
+++ b/ruby/src/main/java/google/ProtobufJavaService.java
@@ -0,0 +1,60 @@
+/*
+ * Protocol Buffers - Google's data interchange format
+ * Copyright 2014 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.
+ */
+
+package google;
+
+import com.google.protobuf.jruby.*;
+import org.jruby.Ruby;
+import org.jruby.runtime.load.BasicLibraryService;
+
+import java.io.IOException;
+
+public class ProtobufJavaService implements BasicLibraryService {
+    @Override
+    public boolean basicLoad(Ruby ruby) throws IOException {
+        ruby.defineModule("Google");
+        RubyProtobuf.createProtobuf(ruby);
+        RubyDescriptor.createRubyDescriptor(ruby);
+        RubyBuilder.createRubyBuilder(ruby);
+        RubyFieldDescriptor.createRubyFileDescriptor(ruby);
+        RubyMessageBuilderContext.createRubyMessageBuilderContext(ruby);
+        RubyEnumDescriptor.createRubyEnumDescriptor(ruby);
+        RubyEnumBuilderContext.createRubyEnumBuilderContext(ruby);
+        RubyDescriptorPool.createRubyDescriptorPool(ruby);
+        RubyRepeatedField.createRubyRepeatedField(ruby);
+        RubyFieldDescriptor.createRubyFileDescriptor(ruby);
+        RubyMap.createRubyMap(ruby);
+        RubyOneofDescriptor.createRubyOneofDescriptor(ruby);
+        RubyOneofBuilderContext.createRubyOneofBuilderContext(ruby);
+        return true;
+    }
+}
diff --git a/ruby/src/main/sentinel.proto b/ruby/src/main/sentinel.proto
new file mode 100644
index 0000000..89a1ae1
--- /dev/null
+++ b/ruby/src/main/sentinel.proto
@@ -0,0 +1,15 @@
+syntax = "proto3";
+package com.google.protobuf.jruby;
+option optimize_for = CODE_SIZE;
+
+message Sentinel {
+  optional int32 default_int32 = 1;
+  optional int64 default_int64 = 2;
+  optional uint32 default_unit32 = 3;
+  optional uint64 default_uint64 = 4;
+  optional string default_string = 5;
+  optional bool default_bool = 6;
+  optional float default_float = 7;
+  optional double default_double = 8;
+  optional bytes default_bytes = 9;
+}
diff --git a/ruby/tests/basic.rb b/ruby/tests/basic.rb
index a78cc39..9d0f0a9 100644
--- a/ruby/tests/basic.rb
+++ b/ruby/tests/basic.rb
@@ -972,6 +972,7 @@
     end
 
     def test_json
+      skip("Unimplemented") if RUBY_PLATFORM == "java"
       m = TestMessage.new(:optional_int32 => 1234,
                           :optional_int64 => -0x1_0000_0000,
                           :optional_uint32 => 0x8000_0000,
@@ -994,6 +995,7 @@
     end
 
     def test_json_maps
+      skip("Unimplemented") if RUBY_PLATFORM == "java"
       m = MapMessage.new(:map_string_int32 => {"a" => 1})
       expected = '{"map_string_int32":{"a":1},"map_string_msg":{}}'
       assert MapMessage.encode_json(m) == expected
diff --git a/ruby/tests/stress.rb b/ruby/tests/stress.rb
index 1bd768e..082d5e2 100644
--- a/ruby/tests/stress.rb
+++ b/ruby/tests/stress.rb
@@ -30,7 +30,7 @@
       100_000.times do
         mnew = TestMessage.decode(data)
         mnew = mnew.dup
-        assert mnew.inspect == m.inspect
+        assert_equal mnew.inspect, m.inspect
         assert TestMessage.encode(mnew) == data
       end
     end
diff --git a/src/Makefile.am b/src/Makefile.am
index f84cd7f..8a27b82 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -321,7 +321,8 @@
   google/protobuf/unittest_preserve_unknown_enum.proto         \
   google/protobuf/unittest_preserve_unknown_enum2.proto        \
   google/protobuf/unittest_proto3_arena.proto                  \
-  google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto
+  google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto  \
+  google/protobuf/compiler/cpp/test_large_enum_value.proto
 
 EXTRA_DIST =                                                   \
   $(protoc_inputs)                                             \
@@ -397,6 +398,8 @@
   google/protobuf/unittest_preserve_unknown_enum2.pb.h         \
   google/protobuf/unittest_proto3_arena.pb.cc                  \
   google/protobuf/unittest_proto3_arena.pb.h                   \
+  google/protobuf/compiler/cpp/test_large_enum_value.pb.cc     \
+  google/protobuf/compiler/cpp/test_large_enum_value.pb.h      \
   google/protobuf/compiler/cpp/cpp_test_bad_identifiers.pb.cc  \
   google/protobuf/compiler/cpp/cpp_test_bad_identifiers.pb.h
 
diff --git a/src/google/protobuf/compiler/command_line_interface.cc b/src/google/protobuf/compiler/command_line_interface.cc
index 0910631..567238a 100644
--- a/src/google/protobuf/compiler/command_line_interface.cc
+++ b/src/google/protobuf/compiler/command_line_interface.cc
@@ -48,7 +48,6 @@
 #include <iostream>
 #include <ctype.h>
 
-#include <google/protobuf/stubs/hash.h>
 #include <memory>
 #ifndef _SHARED_PTR_H
 #include <google/protobuf/stubs/shared_ptr.h>
@@ -255,6 +254,9 @@
   // format, unless one has already been written.
   void AddJarManifest();
 
+  // Get name of all output files.
+  void GetOutputFilenames(vector<string>* output_filenames);
+
   // implements GeneratorContext --------------------------------------
   io::ZeroCopyOutputStream* Open(const string& filename);
   io::ZeroCopyOutputStream* OpenForAppend(const string& filename);
@@ -442,6 +444,14 @@
   }
 }
 
+void CommandLineInterface::GeneratorContextImpl::GetOutputFilenames(
+    vector<string>* output_filenames) {
+  for (map<string, string*>::iterator iter = files_.begin();
+       iter != files_.end(); ++iter) {
+    output_filenames->push_back(iter->first);
+  }
+}
+
 io::ZeroCopyOutputStream* CommandLineInterface::GeneratorContextImpl::Open(
     const string& filename) {
   return new MemoryOutputStream(this, filename, false);
@@ -673,7 +683,6 @@
   // We construct a separate GeneratorContext for each output location.  Note
   // that two code generators may output to the same location, in which case
   // they should share a single GeneratorContext so that OpenForInsert() works.
-  typedef hash_map<string, GeneratorContextImpl*> GeneratorContextMap;
   GeneratorContextMap output_directories;
 
   // Generate output.
@@ -720,6 +729,13 @@
     }
   }
 
+  if (!dependency_out_name_.empty()) {
+    if (!GenerateDependencyManifestFile(parsed_files, output_directories,
+                                        &source_tree)) {
+      return 1;
+    }
+  }
+
   STLDeleteValues(&output_directories);
 
   if (!descriptor_set_name_.empty()) {
@@ -778,6 +794,7 @@
   output_directives_.clear();
   codec_type_.clear();
   descriptor_set_name_.clear();
+  dependency_out_name_.clear();
 
   mode_ = MODE_COMPILE;
   print_mode_ = PRINT_NONE;
@@ -880,6 +897,15 @@
     std::cerr << "Missing output directives." << std::endl;
     return PARSE_ARGUMENT_FAIL;
   }
+  if (mode_ != MODE_COMPILE && !dependency_out_name_.empty()) {
+    cerr << "Can only use --dependency_out=FILE when generating code." << endl;
+    return PARSE_ARGUMENT_FAIL;
+  }
+  if (!dependency_out_name_.empty() && input_files_.size() > 1) {
+    cerr << "Can only process one input file when using --dependency_out=FILE."
+         << endl;
+    return PARSE_ARGUMENT_FAIL;
+  }
   if (imports_in_descriptor_set_ && descriptor_set_name_.empty()) {
     std::cerr << "--include_imports only makes sense when combined with "
                  "--descriptor_set_out." << std::endl;
@@ -1026,6 +1052,17 @@
     }
     descriptor_set_name_ = value;
 
+  } else if (name == "--dependency_out") {
+    if (!dependency_out_name_.empty()) {
+      cerr << name << " may only be passed once." << endl;
+      return PARSE_ARGUMENT_FAIL;
+    }
+    if (value.empty()) {
+      cerr << name << " requires a non-empty value." << endl;
+      return PARSE_ARGUMENT_FAIL;
+    }
+    dependency_out_name_ = value;
+
   } else if (name == "--include_imports") {
     if (imports_in_descriptor_set_) {
       std::cerr << name << " may only be passed once." << std::endl;
@@ -1225,6 +1262,9 @@
 "                              include information about the original\n"
 "                              location of each decl in the source file as\n"
 "                              well as surrounding comments.\n"
+"  --dependency_out=FILE       Write a dependency output file in the format\n"
+"                              expected by make. This writes the transitive\n"
+"                              set of input file paths to FILE\n"
 "  --error_format=FORMAT       Set the format in which to print errors.\n"
 "                              FORMAT may be 'gcc' (the default) or 'msvs'\n"
 "                              (Microsoft Visual Studio format).\n"
@@ -1301,6 +1341,76 @@
   return true;
 }
 
+bool CommandLineInterface::GenerateDependencyManifestFile(
+    const vector<const FileDescriptor*>& parsed_files,
+    const GeneratorContextMap& output_directories,
+    DiskSourceTree* source_tree) {
+  FileDescriptorSet file_set;
+
+  set<const FileDescriptor*> already_seen;
+  for (int i = 0; i < parsed_files.size(); i++) {
+    GetTransitiveDependencies(parsed_files[i],
+                              false,
+                              &already_seen,
+                              file_set.mutable_file());
+  }
+
+  vector<string> output_filenames;
+  for (GeneratorContextMap::const_iterator iter = output_directories.begin();
+       iter != output_directories.end(); ++iter) {
+    const string& location = iter->first;
+    GeneratorContextImpl* directory = iter->second;
+    vector<string> relative_output_filenames;
+    directory->GetOutputFilenames(&relative_output_filenames);
+    for (int i = 0; i < relative_output_filenames.size(); i++) {
+      string output_filename = location + relative_output_filenames[i];
+      if (output_filename.compare(0, 2, "./") == 0) {
+        output_filename = output_filename.substr(2);
+      }
+      output_filenames.push_back(output_filename);
+    }
+  }
+
+  int fd;
+  do {
+    fd = open(dependency_out_name_.c_str(),
+              O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
+  } while (fd < 0 && errno == EINTR);
+
+  if (fd < 0) {
+    perror(dependency_out_name_.c_str());
+    return false;
+  }
+
+  io::FileOutputStream out(fd);
+  io::Printer printer(&out, '$');
+
+  for (int i = 0; i < output_filenames.size(); i++) {
+    printer.Print(output_filenames[i].c_str());
+    if (i == output_filenames.size() - 1) {
+      printer.Print(":");
+    } else {
+      printer.Print(" \\\n");
+    }
+  }
+
+  for (int i = 0; i < file_set.file_size(); i++) {
+    const FileDescriptorProto& file = file_set.file(i);
+    const string& virtual_file = file.name();
+    string disk_file;
+    if (source_tree &&
+        source_tree->VirtualFileToDiskFile(virtual_file, &disk_file)) {
+      printer.Print(" $disk_file$", "disk_file", disk_file);
+      if (i < file_set.file_size() - 1) printer.Print("\\\n");
+    } else {
+      cerr << "Unable to identify path for file " << virtual_file << endl;
+      return false;
+    }
+  }
+
+  return true;
+}
+
 bool CommandLineInterface::GeneratePluginOutput(
     const vector<const FileDescriptor*>& parsed_files,
     const string& plugin_name,
diff --git a/src/google/protobuf/compiler/command_line_interface.h b/src/google/protobuf/compiler/command_line_interface.h
index 74a0adb..7e611c4 100644
--- a/src/google/protobuf/compiler/command_line_interface.h
+++ b/src/google/protobuf/compiler/command_line_interface.h
@@ -39,6 +39,7 @@
 #define GOOGLE_PROTOBUF_COMPILER_COMMAND_LINE_INTERFACE_H__
 
 #include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/hash.h>
 #include <string>
 #include <vector>
 #include <map>
@@ -190,6 +191,7 @@
   class ErrorPrinter;
   class GeneratorContextImpl;
   class MemoryOutputStream;
+  typedef hash_map<string, GeneratorContextImpl*> GeneratorContextMap;
 
   // Clear state from previous Run().
   void Clear();
@@ -247,6 +249,12 @@
   // Implements the --descriptor_set_out option.
   bool WriteDescriptorSet(const vector<const FileDescriptor*> parsed_files);
 
+  // Implements the --dependency_out option
+  bool GenerateDependencyManifestFile(
+      const vector<const FileDescriptor*>& parsed_files,
+      const GeneratorContextMap& output_directories,
+      DiskSourceTree* source_tree);
+
   // Get all transitive dependencies of the given file (including the file
   // itself), adding them to the given list of FileDescriptorProtos.  The
   // protos will be ordered such that every file is listed before any file that
@@ -353,6 +361,10 @@
   // FileDescriptorSet should be written.  Otherwise, empty.
   string descriptor_set_name_;
 
+  // If --dependency_out was given, this is the path to the file where the
+  // dependency file will be written. Otherwise, empty.
+  string dependency_out_name_;
+
   // True if --include_imports was given, meaning that we should
   // write all transitive dependencies to the DescriptorSet.  Otherwise, only
   // the .proto files listed on the command-line are added.
diff --git a/src/google/protobuf/compiler/command_line_interface_unittest.cc b/src/google/protobuf/compiler/command_line_interface_unittest.cc
index 3d27829..2b26f3b 100644
--- a/src/google/protobuf/compiler/command_line_interface_unittest.cc
+++ b/src/google/protobuf/compiler/command_line_interface_unittest.cc
@@ -115,6 +115,11 @@
   // Create a subdirectory within temp_directory_.
   void CreateTempDir(const string& name);
 
+  // Change working directory to temp directory.
+  void SwitchToTempDirectory() {
+    File::ChangeWorkingDirectory(temp_directory_);
+  }
+
   void SetInputsAreProtoPathRelative(bool enable) {
     cli_.SetInputsAreProtoPathRelative(enable);
   }
@@ -179,6 +184,9 @@
   void ReadDescriptorSet(const string& filename,
                          FileDescriptorSet* descriptor_set);
 
+  void ExpectFileContent(const string& filename,
+                         const string& content);
+
  private:
   // The object we are testing.
   CommandLineInterface cli_;
@@ -459,6 +467,17 @@
   EXPECT_EQ(expected_text, captured_stdout_);
 }
 
+
+void CommandLineInterfaceTest::ExpectFileContent(
+    const string& filename, const string& content) {
+  string path = temp_directory_ + "/" + filename;
+  string file_contents;
+  GOOGLE_CHECK_OK(File::GetContents(path, &file_contents, true));
+
+  EXPECT_EQ(StringReplace(content, "$tmpdir", temp_directory_, true),
+            file_contents);
+}
+
 // ===================================================================
 
 TEST_F(CommandLineInterfaceTest, BasicOutput) {
@@ -943,6 +962,71 @@
   EXPECT_TRUE(descriptor_set.file(1).has_source_code_info());
 }
 
+TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFileGivenTwoInputs) {
+  CreateTempFile("foo.proto",
+    "syntax = \"proto2\";\n"
+    "message Foo {}\n");
+  CreateTempFile("bar.proto",
+    "syntax = \"proto2\";\n"
+    "import \"foo.proto\";\n"
+    "message Bar {\n"
+    "  optional Foo foo = 1;\n"
+    "}\n");
+
+  Run("protocol_compiler --dependency_out=$tmpdir/manifest "
+      "--test_out=$tmpdir --proto_path=$tmpdir bar.proto foo.proto");
+
+  ExpectErrorText(
+      "Can only process one input file when using --dependency_out=FILE.\n");
+}
+
+TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFile) {
+  CreateTempFile("foo.proto",
+    "syntax = \"proto2\";\n"
+    "message Foo {}\n");
+  CreateTempFile("bar.proto",
+    "syntax = \"proto2\";\n"
+    "import \"foo.proto\";\n"
+    "message Bar {\n"
+    "  optional Foo foo = 1;\n"
+    "}\n");
+
+  string current_working_directory = get_current_dir_name();
+  SwitchToTempDirectory();
+
+  Run("protocol_compiler --dependency_out=manifest --test_out=. "
+      "bar.proto");
+
+  ExpectNoErrors();
+
+  ExpectFileContent("manifest",
+                    "bar.proto.MockCodeGenerator.test_generator: "
+                    "foo.proto\\\n bar.proto");
+
+  File::ChangeWorkingDirectory(current_working_directory);
+}
+
+TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFileForAbsolutePath) {
+  CreateTempFile("foo.proto",
+    "syntax = \"proto2\";\n"
+    "message Foo {}\n");
+  CreateTempFile("bar.proto",
+    "syntax = \"proto2\";\n"
+    "import \"foo.proto\";\n"
+    "message Bar {\n"
+    "  optional Foo foo = 1;\n"
+    "}\n");
+
+  Run("protocol_compiler --dependency_out=$tmpdir/manifest "
+      "--test_out=$tmpdir --proto_path=$tmpdir bar.proto");
+
+  ExpectNoErrors();
+
+  ExpectFileContent("manifest",
+                    "$tmpdir/bar.proto.MockCodeGenerator.test_generator: "
+                    "$tmpdir/foo.proto\\\n $tmpdir/bar.proto");
+}
+
 // -------------------------------------------------------------------
 
 TEST_F(CommandLineInterfaceTest, ParseErrors) {
diff --git a/src/google/protobuf/compiler/cpp/test_large_enum_value.proto b/src/google/protobuf/compiler/cpp/test_large_enum_value.proto
new file mode 100644
index 0000000..cb6ca1b
--- /dev/null
+++ b/src/google/protobuf/compiler/cpp/test_large_enum_value.proto
@@ -0,0 +1,43 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Test that proto2 compiler can generate valid code when the enum value
+// is INT_MAX. Note that this is a compile-only test and this proto is not
+// referenced in any C++ code.
+syntax = "proto2";
+
+package protobuf_unittest;
+
+message TestLargeEnumValue {
+  enum EnumWithLargeValue {
+    VALUE_1 = 1;
+    VALUE_MAX = 0x7fffffff;
+  }
+}
diff --git a/src/google/protobuf/testing/file.cc b/src/google/protobuf/testing/file.cc
index 5344ec1..3d07b12 100644
--- a/src/google/protobuf/testing/file.cc
+++ b/src/google/protobuf/testing/file.cc
@@ -192,5 +192,9 @@
 #endif
 }
 
+bool File::ChangeWorkingDirectory(const string& new_working_directory) {
+  return chdir(new_working_directory.c_str()) == 0;
+}
+
 }  // namespace protobuf
 }  // namespace google
diff --git a/src/google/protobuf/testing/file.h b/src/google/protobuf/testing/file.h
index d2aeabf..2f63f80 100644
--- a/src/google/protobuf/testing/file.h
+++ b/src/google/protobuf/testing/file.h
@@ -77,6 +77,9 @@
   static void DeleteRecursively(const string& name,
                                 void* dummy1, void* dummy2);
 
+  // Change working directory to given directory.
+  static bool ChangeWorkingDirectory(const string& new_working_directory);
+
   static bool GetContents(
       const string& name, string* output, bool /*is_default*/) {
     return ReadFileToString(name, output);