Integrated internal changes from Google
diff --git a/js/binary/proto_test.js b/js/binary/proto_test.js
index f5e1b6b..1618210 100644
--- a/js/binary/proto_test.js
+++ b/js/binary/proto_test.js
@@ -659,5 +659,6 @@
         'jspb.test.TestAllTypes');
 
     checkAllFields(msg, msg2);
+
   });
 });
diff --git a/js/debug.js b/js/debug.js
index ba51bbe..9655b2e 100644
--- a/js/debug.js
+++ b/js/debug.js
@@ -37,6 +37,7 @@
 goog.require('goog.array');
 goog.require('goog.asserts');
 goog.require('goog.object');
+goog.require('jspb.Map');
 goog.require('jspb.Message');
 
 
@@ -90,6 +91,16 @@
     goog.asserts.assertArray(thing);
     return goog.array.map(thing, jspb.debug.dump_);
   }
+
+  if (message instanceof jspb.Map) {
+    var mapObject = {};
+    var entries = message.entries();
+    for (var entry = entries.next(); !entry.done; entry = entries.next()) {
+      mapObject[entry.value[0]] = jspb.debug.dump_(entry.value[1]);
+    }
+    return mapObject;
+  }
+
   goog.asserts.assert(message instanceof jspb.Message,
       'Only messages expected: ' + thing);
   var ctor = message.constructor;
diff --git a/js/debug_test.js b/js/debug_test.js
index d0d646a..5096869 100644
--- a/js/debug_test.js
+++ b/js/debug_test.js
@@ -38,7 +38,9 @@
 // CommonJS-LoadFromFile: test_pb
 goog.require('proto.jspb.test.HasExtensions');
 goog.require('proto.jspb.test.IsExtension');
+goog.require('proto.jspb.test.MapValueMessageNoBinary');
 goog.require('proto.jspb.test.Simple1');
+goog.require('proto.jspb.test.TestMapFieldsNoBinary');
 
 
 // CommonJS-LoadFromFile: testbinary_pb
@@ -113,4 +115,74 @@
     }, jspb.debug.dump(extendable));
   });
 
+  it('testMapsBasicTypes', function() {
+    if (COMPILED) {
+      return;
+    }
+
+    var message = new proto.jspb.test.TestMapFieldsNoBinary();
+    message.getMapBoolStringMap().set(true, 'bool_string_value1');
+    message.getMapBoolStringMap().set(false, 'bool_string_value2');
+    message.getMapStringInt32Map().set('key', 111);
+
+    assertObjectEquals({
+      '$name': 'proto.jspb.test.TestMapFieldsNoBinary',
+      'mapBoolStringMap': {
+        true: 'bool_string_value1',
+        false: 'bool_string_value2'
+      },
+      'mapInt32StringMap': {},
+      'mapInt64StringMap': {},
+      'mapStringBoolMap': {},
+      'mapStringDoubleMap': {},
+      'mapStringEnumMap': {},
+      'mapStringInt32Map': {
+        'key': 111
+      },
+      'mapStringInt64Map': {},
+      'mapStringMsgMap': {},
+      'mapStringStringMap': {},
+      'mapStringTestmapfieldsMap': {}
+    }, jspb.debug.dump(message));
+  });
+
+  it('testMapsMessageValues', function() {
+    if (COMPILED) {
+      return;
+    }
+
+    var value1 = new proto.jspb.test.MapValueMessageNoBinary();
+    value1.setFoo(1111);
+    var value2 = new proto.jspb.test.MapValueMessageNoBinary();
+    value2.setFoo(2222);
+
+    var message = new proto.jspb.test.TestMapFieldsNoBinary();
+    message.getMapStringMsgMap().set('key1', value1);
+    message.getMapStringMsgMap().set('key2', value2);
+
+    assertObjectEquals({
+      '$name': 'proto.jspb.test.TestMapFieldsNoBinary',
+      'mapBoolStringMap': {},
+      'mapInt32StringMap': {},
+      'mapInt64StringMap': {},
+      'mapStringBoolMap': {},
+      'mapStringDoubleMap': {},
+      'mapStringEnumMap': {},
+      'mapStringInt32Map': {},
+      'mapStringInt64Map': {},
+      'mapStringMsgMap': {
+        'key1': {
+          '$name': 'proto.jspb.test.MapValueMessageNoBinary',
+          'foo': 1111
+        },
+        'key2': {
+          '$name': 'proto.jspb.test.MapValueMessageNoBinary',
+          'foo': 2222
+        }
+      },
+      'mapStringStringMap': {},
+      'mapStringTestmapfieldsMap': {}
+    }, jspb.debug.dump(message));
+  });
+
 });
diff --git a/js/maps_test.js b/js/maps_test.js
index e496f44..4640c98 100755
--- a/js/maps_test.js
+++ b/js/maps_test.js
@@ -340,11 +340,17 @@
     assertElementsEquals(entryIterator.next().value, ['key2', 'value2']);
     assertEquals(entryIterator.next().done, true);
 
-    if (typeof(Symbol) != 'undefined') {
+    try {
       var entryIterable = m.entries()[Symbol.iterator]();
       assertElementsEquals(entryIterable.next().value, ['key1', 'value1']);
       assertElementsEquals(entryIterable.next().value, ['key2', 'value2']);
       assertEquals(entryIterable.next().done, true);
+    } catch (err) {
+      // jspb.Map.ArrayIteratorIterable_.prototype[Symbol.iterator] may be
+      // undefined in some environment.
+      if (err.name != 'TypeError' && err.name != 'ReferenceError') {
+        throw err;
+      }
     }
 
     var keyIterator = m.keys();
@@ -352,22 +358,34 @@
     assertEquals(keyIterator.next().value, 'key2');
     assertEquals(keyIterator.next().done, true);
 
-    if (typeof(Symbol) != 'undefined') {
+    try {
       var keyIterable = m.keys()[Symbol.iterator]();
       assertEquals(keyIterable.next().value, 'key1');
       assertEquals(keyIterable.next().value, 'key2');
       assertEquals(keyIterable.next().done, true);
+    } catch (err) {
+      // jspb.Map.ArrayIteratorIterable_.prototype[Symbol.iterator] may be
+      // undefined in some environment.
+      if (err.name != 'TypeError' && err.name != 'ReferenceError') {
+        throw err;
+      }
     }
     var valueIterator = m.values();
     assertEquals(valueIterator.next().value, 'value1');
     assertEquals(valueIterator.next().value, 'value2');
     assertEquals(valueIterator.next().done, true);
 
-    if (typeof(Symbol) != 'undefined') {
+    try {
       var valueIterable = m.values()[Symbol.iterator]();
       assertEquals(valueIterable.next().value, 'value1');
       assertEquals(valueIterable.next().value, 'value2');
       assertEquals(valueIterable.next().done, true);
+    } catch (err) {
+      // jspb.Map.ArrayIteratorIterable_.prototype[Symbol.iterator] may be
+      // undefined in some environment.
+      if (err.name != 'TypeError' && err.name != 'ReferenceError') {
+        throw err;
+      }
     }
   });
 }
diff --git a/js/message.js b/js/message.js
index 6a37745..17b562e 100644
--- a/js/message.js
+++ b/js/message.js
@@ -427,6 +427,24 @@
                                             goog.isArray(o);
 };
 
+/**
+ * Returns true if the provided argument is an extension object.
+ * @param {*} o The object to classify as array or not.
+ * @return {boolean} True if the provided object is an extension object.
+ * @private
+ */
+jspb.Message.isExtensionObject_ = function(o) {
+  // Normal fields are never objects, so we can be sure that if we find an
+  // object here, then it's the extension object. However, we must ensure that
+  // the object is not an array, since arrays are valid field values (bytes
+  // fields can also be array).
+  // NOTE(lukestebbing): We avoid looking at .length to avoid a JIT bug
+  // in Safari on iOS 8. See the description of CL/86511464 for details.
+  return (o !== null && typeof o == 'object' &&
+      !jspb.Message.isArray_(o) &&
+      !(jspb.Message.SUPPORTS_UINT8ARRAY_ && o instanceof Uint8Array));
+};
+
 
 /**
  * If the array contains an extension object in its last position, then the
@@ -452,13 +470,7 @@
   if (msgLength) {
     lastIndex = msgLength - 1;
     var obj = msg.array[lastIndex];
-    // Normal fields are never objects, so we can be sure that if we find an
-    // object here, then it's the extension object. However, we must ensure that
-    // the object is not an array, since arrays are valid field values.
-    // NOTE(lukestebbing): We avoid looking at .length to avoid a JIT bug
-    // in Safari on iOS 8. See the description of CL/86511464 for details.
-    if (obj && typeof obj == 'object' && !jspb.Message.isArray_(obj) &&
-        !(jspb.Message.SUPPORTS_UINT8ARRAY_ && obj instanceof Uint8Array)) {
+    if (jspb.Message.isExtensionObject_(obj)) {
       msg.pivot_ = jspb.Message.getFieldNumber_(msg, lastIndex);
       msg.extensionObject_ = obj;
       return;
diff --git a/js/message_test.js b/js/message_test.js
index 2bfec62..c237d06 100644
--- a/js/message_test.js
+++ b/js/message_test.js
@@ -33,7 +33,6 @@
 goog.setTestOnly();
 
 goog.require('goog.json');
-goog.require('goog.string');
 goog.require('goog.testing.PropertyReplacer');
 goog.require('goog.testing.asserts');
 goog.require('goog.userAgent');
@@ -41,6 +40,38 @@
 // CommonJS-LoadFromFile: google-protobuf jspb
 goog.require('jspb.Message');
 
+// CommonJS-LoadFromFile: test15_pb proto.jspb.filenametest.package1
+goog.require('proto.jspb.filenametest.package1.b');
+
+// CommonJS-LoadFromFile: test14_pb proto.jspb.filenametest.package2
+goog.require('proto.jspb.filenametest.package2.TestMessage');
+
+// CommonJS-LoadFromFile: test13_pb proto.jspb.filenametest.package1
+goog.require('proto.jspb.filenametest.package1.a');
+goog.require('proto.jspb.filenametest.package1.TestMessage');
+
+// CommonJS-LoadFromFile: test12_pb proto.jspb.circulartest
+goog.require('proto.jspb.circulartest.ExtensionContainingType1');
+goog.require('proto.jspb.circulartest.ExtensionContainingType2');
+goog.require('proto.jspb.circulartest.ExtensionField1');
+goog.require('proto.jspb.circulartest.ExtensionField2');
+goog.require('proto.jspb.circulartest.ExtensionField3');
+goog.require('proto.jspb.circulartest.MapField1');
+goog.require('proto.jspb.circulartest.MapField2');
+goog.require('proto.jspb.circulartest.MessageField1');
+goog.require('proto.jspb.circulartest.MessageField2');
+goog.require('proto.jspb.circulartest.NestedEnum1');
+goog.require('proto.jspb.circulartest.NestedEnum2');
+goog.require('proto.jspb.circulartest.NestedMessage1');
+goog.require('proto.jspb.circulartest.NestedMessage2');
+goog.require('proto.jspb.circulartest.RepeatedMessageField1');
+goog.require('proto.jspb.circulartest.RepeatedMessageField2');
+
+// CommonJS-LoadFromFile: test11_pb proto.jspb.exttest.reverse
+goog.require('proto.jspb.exttest.reverse.TestExtensionReverseOrderMessage1');
+goog.require('proto.jspb.exttest.reverse.TestExtensionReverseOrderMessage2');
+goog.require('proto.jspb.exttest.reverse.c');
+
 // CommonJS-LoadFromFile: test8_pb proto.jspb.exttest.nested
 goog.require('proto.jspb.exttest.nested.TestNestedExtensionsMessage');
 goog.require('proto.jspb.exttest.nested.TestOuterMessage');
@@ -77,6 +108,7 @@
 goog.require('proto.jspb.test.TestEndsWithBytes');
 goog.require('proto.jspb.test.TestGroup');
 goog.require('proto.jspb.test.TestGroup1');
+goog.require('proto.jspb.test.TestLastFieldBeforePivot');
 goog.require('proto.jspb.test.TestMessageWithOneof');
 goog.require('proto.jspb.test.TestReservedNames');
 goog.require('proto.jspb.test.TestReservedNamesExtension');
@@ -284,42 +316,6 @@
     assertFalse(response.hasEnumField());
   });
 
-  it('testClearFields', function() {
-    var data = ['str', true, [11], [[22], [33]], ['s1', 's2']];
-    var foo = new proto.jspb.test.OptionalFields(data);
-    foo.clearAString();
-    foo.clearABool();
-    foo.clearANestedMessage();
-    foo.clearARepeatedMessageList();
-    foo.clearARepeatedStringList();
-    assertEquals('', foo.getAString());
-    assertEquals(false, foo.getABool());
-    assertUndefined(foo.getANestedMessage());
-    assertFalse(foo.hasAString());
-    assertFalse(foo.hasABool());
-    assertObjectEquals([], foo.getARepeatedMessageList());
-    assertObjectEquals([], foo.getARepeatedStringList());
-    // NOTE: We want the missing fields in 'expected' to be undefined,
-    // but we actually get a sparse array instead. We could use something
-    // like [1,undefined,2] to avoid this, except that this is still
-    // sparse on IE. No comment...
-    var expected = [,,, [], []];
-    expected[0] = expected[1] = expected[2] = undefined;
-    assertObjectEquals(expected, foo.toArray());
-  });
-
-  it('testDifferenceRawObject', /** @suppress {visibility} */ function() {
-    var p1 = new proto.jspb.test.HasExtensions(['hi', 'diff', {}]);
-    var p2 = new proto.jspb.test.HasExtensions(['hi', 'what',
-                                               {1000: 'unique'}]);
-    var diff = /** @type {proto.jspb.test.HasExtensions} */
-        (jspb.Message.difference(p1, p2));
-    assertEquals('', diff.getStr1());
-    assertEquals('what', diff.getStr2());
-    assertEquals('', diff.getStr3());
-    assertEquals('unique', diff.extensionObject_[1000]);
-  });
-
   it('testEqualsSimple', function() {
     var s1 = new proto.jspb.test.Simple1(['hi']);
     assertTrue(jspb.Message.equals(s1, new proto.jspb.test.Simple1(['hi'])));
@@ -419,6 +415,12 @@
         ['hi',,, {100: [{200: 'a'}]}], ['hi', {100: [{200: 'a'}]}]));
   });
 
+  it('testInitializeMessageWithLastFieldNull', function() {
+    // This tests for regression to bug http://b/117298778
+    var msg = new proto.jspb.test.TestLastFieldBeforePivot([null]);
+    assertNotUndefined(msg.getLastFieldBeforePivot());
+  });
+
   it('testEqualsNonFinite', function() {
     assertTrue(jspb.Message.compareFields(NaN, NaN));
     assertTrue(jspb.Message.compareFields(NaN, 'NaN'));
@@ -787,71 +789,6 @@
         message.getRecursiveOneofCase());
   });
 
-  it('testInitializeMessageWithSingleValueSetInOneof', function() {
-    var message = new proto.jspb.test.TestMessageWithOneof([,, 'x']);
-
-    assertEquals('x', message.getPone());
-    assertEquals('', message.getPthree());
-    assertEquals(
-        proto.jspb.test.TestMessageWithOneof.PartialOneofCase.PONE,
-        message.getPartialOneofCase());
-  });
-
-  it('testKeepsLastWireValueSetInUnion_multipleValues', function() {
-    var message = new proto.jspb.test.TestMessageWithOneof([,, 'x',, 'y']);
-
-    assertEquals('', message.getPone());
-    assertEquals('y', message.getPthree());
-    assertEquals(
-        proto.jspb.test.TestMessageWithOneof.PartialOneofCase.PTHREE,
-        message.getPartialOneofCase());
-  });
-
-  it('testSettingOneofFieldClearsOthers', function() {
-    var message = new proto.jspb.test.TestMessageWithOneof;
-    assertEquals('', message.getPone());
-    assertEquals('', message.getPthree());
-    assertFalse(message.hasPone());
-    assertFalse(message.hasPthree());
-
-    message.setPone('hi');
-    assertEquals('hi', message.getPone());
-    assertEquals('', message.getPthree());
-    assertTrue(message.hasPone());
-    assertFalse(message.hasPthree());
-
-    message.setPthree('bye');
-    assertEquals('', message.getPone());
-    assertEquals('bye', message.getPthree());
-    assertFalse(message.hasPone());
-    assertTrue(message.hasPthree());
-  });
-
-  it('testSettingOneofFieldDoesNotClearFieldsFromOtherUnions', function() {
-    var other = new proto.jspb.test.TestMessageWithOneof;
-    var message = new proto.jspb.test.TestMessageWithOneof;
-    assertEquals('', message.getPone());
-    assertEquals('', message.getPthree());
-    assertUndefined(message.getRone());
-    assertFalse(message.hasPone());
-    assertFalse(message.hasPthree());
-
-    message.setPone('hi');
-    message.setRone(other);
-    assertEquals('hi', message.getPone());
-    assertEquals('', message.getPthree());
-    assertEquals(other, message.getRone());
-    assertTrue(message.hasPone());
-    assertFalse(message.hasPthree());
-
-    message.setPthree('bye');
-    assertEquals('', message.getPone());
-    assertEquals('bye', message.getPthree());
-    assertEquals(other, message.getRone());
-    assertFalse(message.hasPone());
-    assertTrue(message.hasPthree());
-  });
-
   it('testUnsetsOneofCaseWhenFieldIsCleared', function() {
     var message = new proto.jspb.test.TestMessageWithOneof;
     assertEquals(
@@ -871,178 +808,6 @@
         message.getPartialOneofCase());
   });
 
-  it('testMessageWithDefaultOneofValues', function() {
-    var message = new proto.jspb.test.TestMessageWithOneof;
-    assertEquals(1234, message.getAone());
-    assertEquals(0, message.getAtwo());
-    assertEquals(
-        proto.jspb.test.TestMessageWithOneof.DefaultOneofACase
-            .DEFAULT_ONEOF_A_NOT_SET,
-        message.getDefaultOneofACase());
-
-    message.setAone(567);
-    assertEquals(567, message.getAone());
-    assertEquals(0, message.getAtwo());
-    assertEquals(
-        proto.jspb.test.TestMessageWithOneof.DefaultOneofACase.AONE,
-        message.getDefaultOneofACase());
-
-    message.setAtwo(890);
-    assertEquals(1234, message.getAone());
-    assertEquals(890, message.getAtwo());
-    assertEquals(
-        proto.jspb.test.TestMessageWithOneof.DefaultOneofACase.ATWO,
-        message.getDefaultOneofACase());
-
-    message.clearAtwo();
-    assertEquals(1234, message.getAone());
-    assertEquals(0, message.getAtwo());
-    assertEquals(
-        proto.jspb.test.TestMessageWithOneof.DefaultOneofACase
-            .DEFAULT_ONEOF_A_NOT_SET,
-        message.getDefaultOneofACase());
-  });
-
-  it('testMessageWithDefaultOneofValues_defaultNotOnFirstField', function() {
-    var message = new proto.jspb.test.TestMessageWithOneof;
-    assertEquals(0, message.getBone());
-    assertEquals(1234, message.getBtwo());
-    assertFalse(message.hasBone());
-    assertFalse(message.hasBtwo());
-    assertEquals(
-        proto.jspb.test.TestMessageWithOneof.DefaultOneofBCase
-            .DEFAULT_ONEOF_B_NOT_SET,
-        message.getDefaultOneofBCase());
-
-    message.setBone(2);
-    assertEquals(2, message.getBone());
-    assertEquals(1234, message.getBtwo());
-    assertTrue(message.hasBone());
-    assertFalse(message.hasBtwo());
-    assertEquals(
-        proto.jspb.test.TestMessageWithOneof.DefaultOneofBCase.BONE,
-        message.getDefaultOneofBCase());
-
-    message.setBtwo(3);
-    assertEquals(0, message.getBone());
-    assertFalse(message.hasBone());
-    assertTrue(message.hasBtwo());
-    assertEquals(3, message.getBtwo());
-    assertEquals(
-        proto.jspb.test.TestMessageWithOneof.DefaultOneofBCase.BTWO,
-        message.getDefaultOneofBCase());
-
-    message.clearBtwo();
-    assertEquals(0, message.getBone());
-    assertFalse(message.hasBone());
-    assertFalse(message.hasBtwo());
-    assertEquals(1234, message.getBtwo());
-    assertEquals(
-        proto.jspb.test.TestMessageWithOneof.DefaultOneofBCase
-            .DEFAULT_ONEOF_B_NOT_SET,
-        message.getDefaultOneofBCase());
-  });
-
-  it('testInitializeMessageWithOneofDefaults', function() {
-    var message =
-        new proto.jspb.test.TestMessageWithOneof(new Array(9).concat(567));
-    assertEquals(567, message.getAone());
-    assertEquals(0, message.getAtwo());
-    assertEquals(
-        proto.jspb.test.TestMessageWithOneof.DefaultOneofACase.AONE,
-        message.getDefaultOneofACase());
-
-    message =
-        new proto.jspb.test.TestMessageWithOneof(new Array(10).concat(890));
-    assertEquals(1234, message.getAone());
-    assertEquals(890, message.getAtwo());
-    assertEquals(
-        proto.jspb.test.TestMessageWithOneof.DefaultOneofACase.ATWO,
-        message.getDefaultOneofACase());
-
-    message =
-        new proto.jspb.test.TestMessageWithOneof(new Array(9).concat(567, 890));
-    assertEquals(1234, message.getAone());
-    assertEquals(890, message.getAtwo());
-    assertEquals(
-        proto.jspb.test.TestMessageWithOneof.DefaultOneofACase.ATWO,
-        message.getDefaultOneofACase());
-  });
-
-  it('testInitializeMessageWithOneofDefaults_defaultNotSetOnFirstField',
-      function() {
-        var message;
-
-        message =
-            new proto.jspb.test.TestMessageWithOneof(new Array(11).concat(567));
-        assertEquals(567, message.getBone());
-        assertEquals(1234, message.getBtwo());
-        assertEquals(
-            proto.jspb.test.TestMessageWithOneof.DefaultOneofBCase.BONE,
-            message.getDefaultOneofBCase());
-
-        message =
-            new proto.jspb.test.TestMessageWithOneof(new Array(12).concat(890));
-        assertEquals(0, message.getBone());
-        assertEquals(890, message.getBtwo());
-        assertEquals(
-            proto.jspb.test.TestMessageWithOneof.DefaultOneofBCase.BTWO,
-            message.getDefaultOneofBCase());
-
-        message = new proto.jspb.test.TestMessageWithOneof(
-            new Array(11).concat(567, 890));
-        assertEquals(0, message.getBone());
-        assertEquals(890, message.getBtwo());
-        assertEquals(
-            proto.jspb.test.TestMessageWithOneof.DefaultOneofBCase.BTWO,
-            message.getDefaultOneofBCase());
-      });
-
-  it('testOneofContainingAnotherMessage', function() {
-    var message = new proto.jspb.test.TestMessageWithOneof;
-    assertEquals(
-        proto.jspb.test.TestMessageWithOneof.RecursiveOneofCase.
-            RECURSIVE_ONEOF_NOT_SET,
-        message.getRecursiveOneofCase());
-
-    var other = new proto.jspb.test.TestMessageWithOneof;
-    message.setRone(other);
-    assertEquals(other, message.getRone());
-    assertEquals('', message.getRtwo());
-    assertEquals(
-        proto.jspb.test.TestMessageWithOneof.RecursiveOneofCase.RONE,
-        message.getRecursiveOneofCase());
-
-    message.setRtwo('hi');
-    assertUndefined(message.getRone());
-    assertEquals('hi', message.getRtwo());
-    assertEquals(
-        proto.jspb.test.TestMessageWithOneof.RecursiveOneofCase.RTWO,
-        message.getRecursiveOneofCase());
-  });
-
-  it('testQueryingOneofCaseEnsuresOnlyOneFieldIsSetInUnderlyingArray',
-     function() {
-    var message = new proto.jspb.test.TestMessageWithOneof;
-    message.setPone('x');
-    assertEquals('x', message.getPone());
-    assertEquals('', message.getPthree());
-    assertEquals(
-        proto.jspb.test.TestMessageWithOneof.PartialOneofCase.PONE,
-        message.getPartialOneofCase());
-
-    var array = message.toArray();
-    assertEquals('x', array[2]);
-    assertUndefined(array[4]);
-    array[4] = 'y';
-
-    assertEquals(
-        proto.jspb.test.TestMessageWithOneof.PartialOneofCase.PTHREE,
-        message.getPartialOneofCase());
-    assertUndefined(array[2]);
-    assertEquals('y', array[4]);
-  });
-
   it('testFloatingPointFieldsSupportNan', function() {
     var assertNan = function(x) {
       assertTrue('Expected ' + x + ' (' + goog.typeOf(x) + ') to be NaN.',
@@ -1065,4 +830,170 @@
     assertNan(message.getDefaultDoubleField());
   });
 
+  it('testExtensionReverseOrder', function() {
+    var message2 =
+        new proto.jspb.exttest.reverse.TestExtensionReverseOrderMessage2;
+
+    message2.setExtension(
+        proto.jspb.exttest.reverse.TestExtensionReverseOrderMessage1.a, 233);
+    message2.setExtension(
+        proto
+            .jspb
+            .exttest
+            .reverse
+            .TestExtensionReverseOrderMessage1
+            .TestExtensionReverseOrderNestedMessage1.b, 2333);
+    message2.setExtension(proto.jspb.exttest.reverse.c, 23333);
+
+    assertEquals(
+        233,
+        message2.getExtension(
+            proto.jspb.exttest.reverse.TestExtensionReverseOrderMessage1.a));
+    assertEquals(
+        2333,
+        message2.getExtension(
+            proto
+                .jspb
+                .exttest
+                .reverse
+                .TestExtensionReverseOrderMessage1
+                .TestExtensionReverseOrderNestedMessage1.b));
+    assertEquals(
+        23333,
+        message2.getExtension(proto.jspb.exttest.reverse.c));
+  });
+
+  it('testCircularDepsBaseOnMessageField', function() {
+    var nestMessage1 = new proto.jspb.circulartest.MessageField1;
+    var nestMessage2 = new proto.jspb.circulartest.MessageField2;
+    var message1 = new proto.jspb.circulartest.MessageField1;
+    var message2 = new proto.jspb.circulartest.MessageField2;
+
+    nestMessage1.setA(1);
+    nestMessage2.setA(2);
+    message1.setB(nestMessage2);
+    message2.setB(nestMessage1);
+
+
+    assertEquals(2, message1.getB().getA());
+    assertEquals(1, message2.getB().getA());
+  });
+
+
+  it('testCircularDepsBaseOnRepeatedMessageField', function() {
+    var nestMessage1 = new proto.jspb.circulartest.RepeatedMessageField1;
+    var nestMessage2 = new proto.jspb.circulartest.RepeatedMessageField2;
+    var message1 = new proto.jspb.circulartest.RepeatedMessageField1;
+    var message2 = new proto.jspb.circulartest.RepeatedMessageField2;
+
+    nestMessage1.setA(1);
+    nestMessage2.setA(2);
+    message1.setB(nestMessage2);
+    message2.addB(nestMessage1);
+
+
+    assertEquals(2, message1.getB().getA());
+    assertEquals(1, message2.getBList()[0].getA());
+  });
+
+  it('testCircularDepsBaseOnMapField', function() {
+    var nestMessage1 = new proto.jspb.circulartest.MapField1;
+    var nestMessage2 = new proto.jspb.circulartest.MapField2;
+    var message1 = new proto.jspb.circulartest.MapField1;
+    var message2 = new proto.jspb.circulartest.MapField2;
+
+    nestMessage1.setA(1);
+    nestMessage2.setA(2);
+    message1.setB(nestMessage2);
+    message2.getBMap().set(1, nestMessage1);
+
+
+    assertEquals(2, message1.getB().getA());
+    assertEquals(1, message2.getBMap().get(1).getA());
+  });
+
+  it('testCircularDepsBaseOnNestedMessage', function() {
+    var nestMessage1 =
+        new proto.jspb.circulartest.NestedMessage1.NestedNestedMessage;
+    var nestMessage2 = new proto.jspb.circulartest.NestedMessage2;
+    var message1 = new proto.jspb.circulartest.NestedMessage1;
+    var message2 = new proto.jspb.circulartest.NestedMessage2;
+
+    nestMessage1.setA(1);
+    nestMessage2.setA(2);
+    message1.setB(nestMessage2);
+    message2.setB(nestMessage1);
+
+
+    assertEquals(2, message1.getB().getA());
+    assertEquals(1, message2.getB().getA());
+  });
+
+  it('testCircularDepsBaseOnNestedEnum', function() {
+    var nestMessage2 = new proto.jspb.circulartest.NestedEnum2;
+    var message1 = new proto.jspb.circulartest.NestedEnum1;
+    var message2 = new proto.jspb.circulartest.NestedEnum2;
+
+    nestMessage2.setA(2);
+    message1.setB(nestMessage2);
+    message2.setB(proto.jspb.circulartest.NestedEnum1.NestedNestedEnum.VALUE_1);
+
+
+    assertEquals(2, message1.getB().getA());
+    assertEquals(
+        proto.jspb.circulartest.NestedEnum1.NestedNestedEnum.VALUE_1,
+        message2.getB());
+  });
+
+  it('testCircularDepsBaseOnExtensionContainingType', function() {
+    var nestMessage2 = new proto.jspb.circulartest.ExtensionContainingType2;
+    var message1 = new proto.jspb.circulartest.ExtensionContainingType1;
+
+    nestMessage2.setA(2);
+    message1.setB(nestMessage2);
+    message1.setExtension(
+        proto.jspb.circulartest.ExtensionContainingType2.c, 1);
+
+
+    assertEquals(2, message1.getB().getA());
+    assertEquals(
+        1,
+        message1.getExtension(
+            proto.jspb.circulartest.ExtensionContainingType2.c));
+  });
+
+  it('testCircularDepsBaseOnExtensionField', function() {
+    var nestMessage2 = new proto.jspb.circulartest.ExtensionField2;
+    var message1 = new proto.jspb.circulartest.ExtensionField1;
+    var message3 = new proto.jspb.circulartest.ExtensionField3;
+
+    nestMessage2.setA(2);
+    message1.setB(nestMessage2);
+    message3.setExtension(proto.jspb.circulartest.ExtensionField2.c, message1);
+
+
+    assertEquals(
+        2,
+        message3.getExtension(proto.jspb.circulartest.ExtensionField2.c)
+            .getB()
+            .getA());
+  });
+
+  it('testSameMessageNameOuputs', function() {
+    var package1Message = new proto.jspb.filenametest.package1.TestMessage;
+    var package2Message = new proto.jspb.filenametest.package2.TestMessage;
+
+    package1Message.setExtension(
+        proto.jspb.filenametest.package1.a, 10);
+    package1Message.setExtension(
+        proto.jspb.filenametest.package1.b, 11);
+    package2Message.setA(12);
+
+    assertEquals(10,
+        package1Message.getExtension(proto.jspb.filenametest.package1.a));
+    assertEquals(11,
+        package1Message.getExtension(proto.jspb.filenametest.package1.b));
+    assertEquals(12, package2Message.getA());
+  });
+
 });
diff --git a/js/proto3_test.js b/js/proto3_test.js
index 4aed88b..7f9d71d 100644
--- a/js/proto3_test.js
+++ b/js/proto3_test.js
@@ -30,17 +30,15 @@
 
 goog.require('goog.crypt.base64');
 goog.require('goog.testing.asserts');
-
 // CommonJS-LoadFromFile: testbinary_pb proto.jspb.test
 goog.require('proto.jspb.test.ForeignMessage');
-
 // CommonJS-LoadFromFile: proto3_test_pb proto.jspb.test
 goog.require('proto.jspb.test.Proto3Enum');
 goog.require('proto.jspb.test.TestProto3');
-
+// CommonJS-LoadFromFile: google/protobuf/any_pb proto.google.protobuf
+goog.require('proto.google.protobuf.Any');
 // CommonJS-LoadFromFile: google/protobuf/timestamp_pb proto.google.protobuf
 goog.require('proto.google.protobuf.Timestamp');
-
 // CommonJS-LoadFromFile: google/protobuf/struct_pb proto.google.protobuf
 goog.require('proto.google.protobuf.Struct');
 
@@ -377,6 +375,7 @@
 
   });
 
+
   it('testTimestampWellKnownType', function() {
     var msg = new proto.google.protobuf.Timestamp();
     msg.fromDate(new Date(123456789));
@@ -384,6 +383,9 @@
     assertEquals(789000000, msg.getNanos());
     var date = msg.toDate();
     assertEquals(123456789, date.getTime());
+    var anotherMsg = proto.google.protobuf.Timestamp.fromDate(date);
+    assertEquals(msg.getSeconds(), anotherMsg.getSeconds());
+    assertEquals(msg.getNanos(), anotherMsg.getNanos());
   });
 
   it('testStructWellKnownType', function() {
diff --git a/js/test.proto b/js/test.proto
index 3b538b5..454fd83 100644
--- a/js/test.proto
+++ b/js/test.proto
@@ -241,6 +241,17 @@
   optional bytes data = 2;
 }
 
+// This message is for testing extension handling doesn't affect fields before
+// pivot. Don't add new field to this message. See b/117298778 for more detail.
+message TestLastFieldBeforePivot {
+  optional int32 last_field_before_pivot = 1;
+  extensions 100 to max;
+}
+
+extend TestLastFieldBeforePivot {
+  optional int32 extend_test_last_field_before_pivot_field = 101;
+}
+
 
 message Int64Types {
   optional int64 int64_normal = 1 [jstype=JS_NORMAL];
diff --git a/js/test11.proto b/js/test11.proto
new file mode 100644
index 0000000..ae65a46
--- /dev/null
+++ b/js/test11.proto
@@ -0,0 +1,52 @@
+// 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.
+
+syntax = "proto2";
+
+package jspb.exttest.reverse;
+
+message TestExtensionReverseOrderMessage1 {
+  extend TestExtensionReverseOrderMessage2 {
+    optional int32 a = 1;
+  }
+  message TestExtensionReverseOrderNestedMessage1 {
+    extend TestExtensionReverseOrderMessage2 {
+      optional int32 b = 2;
+    }
+  }
+}
+
+extend TestExtensionReverseOrderMessage2 {
+  optional int32 c = 3;
+}
+
+message TestExtensionReverseOrderMessage2 {
+  extensions 1 to 100;
+}
diff --git a/js/test12.proto b/js/test12.proto
new file mode 100644
index 0000000..4ece1d0
--- /dev/null
+++ b/js/test12.proto
@@ -0,0 +1,119 @@
+// 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.
+
+syntax = "proto2";
+
+
+package jspb.circulartest;
+
+message MessageField1 {
+  optional int32 a = 1;
+  optional MessageField2 b = 2;
+}
+
+message MessageField2 {
+  optional int32 a = 1;
+  optional MessageField1 b = 2;
+}
+
+
+message RepeatedMessageField1 {
+  optional int32 a = 1;
+  optional RepeatedMessageField2 b = 2;
+}
+
+message RepeatedMessageField2 {
+  optional int32 a = 1;
+  repeated RepeatedMessageField1 b = 2;
+}
+
+message MapField1 {
+  optional int32 a = 1;
+  optional MapField2 b = 2;
+}
+
+message MapField2 {
+  optional int32 a = 1;
+  map<int32, MapField1> b = 2;
+}
+
+message NestedMessage1 {
+  optional NestedMessage2 b = 2;
+  message NestedNestedMessage {
+    optional int32 a = 1;
+  }
+}
+
+message NestedMessage2 {
+  optional int32 a = 1;
+  optional NestedMessage1.NestedNestedMessage b = 2;
+}
+
+message NestedEnum1 {
+  optional NestedEnum2 b = 2;
+  enum NestedNestedEnum {
+    UNDEFINED = 0;
+    VALUE_1 = 1;
+  }
+}
+
+message NestedEnum2 {
+  optional int32 a = 1;
+  optional NestedEnum1.NestedNestedEnum b = 2;
+}
+
+message ExtensionContainingType1 {
+  optional int32 a = 1;
+  optional ExtensionContainingType2 b = 2;
+  extensions 99 to 100;
+}
+
+message ExtensionContainingType2 {
+  optional int32 a = 1;
+  extend ExtensionContainingType1 {
+    optional int32 c = 99;
+  }
+}
+
+message ExtensionField1 {
+  optional int32 a = 1;
+  optional ExtensionField2 b = 2;
+}
+
+message ExtensionField2 {
+  optional int32 a = 1;
+  extend ExtensionField3 {
+    optional ExtensionField1 c = 99;
+  }
+}
+
+message ExtensionField3 {
+  extensions 99 to 100;
+}
diff --git a/js/test13.proto b/js/test13.proto
new file mode 100644
index 0000000..4f9d272
--- /dev/null
+++ b/js/test13.proto
@@ -0,0 +1,70 @@
+// 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.
+
+syntax = "proto2";
+
+package jspb.filenametest.package1;
+
+message TestMessage {
+  extensions 1 to 100;
+}
+
+extend TestMessage {
+  optional int32 a = 1;
+}
+
+enum TestEnum {
+  VALUE_0 = 0;
+  VALUE_1 = 1;
+}
+
+message TestLooooooooooooooooooooooooooooooooooooooooooooooooooooooongName1 {
+  optional TestLooooooooooooooooooooooooooooooooooooooooooooooooooooooongName2
+    a = 1;
+  optional int32 b = 2;
+}
+
+message TestLooooooooooooooooooooooooooooooooooooooooooooooooooooooongName2 {
+  optional TestLooooooooooooooooooooooooooooooooooooooooooooooooooooooongName3
+    a = 1;
+  optional int32 b = 2;
+}
+
+message TestLooooooooooooooooooooooooooooooooooooooooooooooooooooooongName3 {
+  optional TestLooooooooooooooooooooooooooooooooooooooooooooooooooooooongName4
+    a = 1;
+  optional int32 b = 2;
+}
+
+message TestLooooooooooooooooooooooooooooooooooooooooooooooooooooooongName4 {
+  optional TestLooooooooooooooooooooooooooooooooooooooooooooooooooooooongName1
+    a = 1;
+  optional int32 b = 2;
+}
diff --git a/js/test14.proto b/js/test14.proto
new file mode 100644
index 0000000..2447eb1
--- /dev/null
+++ b/js/test14.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.
+
+syntax = "proto2";
+
+package jspb.filenametest.package2;
+
+message TestMessage {
+  optional int32 a = 1;
+}
+
+enum TestEnum {
+  VALUE_0 = 0;
+  VALUE_1 = 1;
+  VALUE_2 = 2;
+}
diff --git a/js/test15.proto b/js/test15.proto
new file mode 100644
index 0000000..602cc2d
--- /dev/null
+++ b/js/test15.proto
@@ -0,0 +1,39 @@
+// 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.
+
+syntax = "proto2";
+
+import "test13.proto";
+
+package jspb.filenametest.package1;
+
+extend TestMessage {
+  optional int32 b = 2;
+}