Sync changes from internal repo. (#82)

* Added fast getters for common types.
* Only pass index instead of both tag and index to accessors.
* Delegate more methods to underlying list in PbList.

Fixes #76.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 00018f2..35a1bc2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,10 @@
+## 0.6.1
+
+* Added fast getters for common types.
+* Only pass index instead of both tag and index to accessors.
+* Delegate more methods to underlying list in PbList.
+* Small fixes for Dart 2.0.
+
 ## 0.6.0
 
 * Added enumValues to FieldInfo. Fixes #63.
diff --git a/lib/meta.dart b/lib/meta.dart
index 340817b..6be707d 100644
--- a/lib/meta.dart
+++ b/lib/meta.dart
@@ -50,6 +50,9 @@
   'unknownFields',
   'clone',
   r'$_get',
+  r'$_getI64',
+  r'$_getN',
+  r'$_getS',
   r'$_has',
   r'$_setBool',
   r'$_setBytes',
diff --git a/lib/protobuf.dart b/lib/protobuf.dart
index 6c4965e..a6d5c1e 100644
--- a/lib/protobuf.dart
+++ b/lib/protobuf.dart
@@ -5,7 +5,7 @@
 library protobuf;
 
 import 'dart:async' show Future;
-import 'dart:collection' show ListMixin;
+import 'dart:collection' show ListBase;
 import 'dart:convert' show BASE64, JSON, Utf8Codec;
 import 'dart:math' as math;
 import 'dart:typed_data' show TypedData, Uint8List, ByteData, Endianness;
diff --git a/lib/src/protobuf/builder_info.dart b/lib/src/protobuf/builder_info.dart
index e9d55f9..dec83b9 100644
--- a/lib/src/protobuf/builder_info.dart
+++ b/lib/src/protobuf/builder_info.dart
@@ -7,6 +7,7 @@
 /// Per-message type setup.
 class BuilderInfo {
   final String messageName;
+  final List<FieldInfo> byIndex = <FieldInfo>[];
   final Map<int, FieldInfo> fieldInfo = new Map<int, FieldInfo>();
   final Map<String, FieldInfo> byTagAsString = <String, FieldInfo>{};
   final Map<String, FieldInfo> byName = <String, FieldInfo>{};
@@ -24,9 +25,9 @@
       CreateBuilderFunc subBuilder,
       ValueOfFunc valueOf,
       List<ProtobufEnum> enumValues) {
-    var index = fieldInfo.length;
-    addField(new FieldInfo<T>(name, tagNumber, index, fieldType, defaultOrMaker,
-        subBuilder, valueOf, enumValues));
+    var index = byIndex.length;
+    _addField(new FieldInfo<T>(name, tagNumber, index, fieldType,
+        defaultOrMaker, subBuilder, valueOf, enumValues));
   }
 
   void addRepeated<T>(
@@ -37,12 +38,14 @@
       CreateBuilderFunc subBuilder,
       ValueOfFunc valueOf,
       List<ProtobufEnum> enumValues) {
-    var index = fieldInfo.length;
-    addField(new FieldInfo<T>.repeated(name, tagNumber, index, fieldType, check,
-        subBuilder, valueOf, enumValues));
+    var index = byIndex.length;
+    _addField(new FieldInfo<T>.repeated(name, tagNumber, index, fieldType,
+        check, subBuilder, valueOf, enumValues));
   }
 
-  void addField(FieldInfo fi) {
+  void _addField(FieldInfo fi) {
+    byIndex.add(fi);
+    assert(byIndex[fi.index] == fi);
     fieldInfo[fi.tagNumber] = fi;
     byTagAsString["${fi.tagNumber}"] = fi;
     byName[fi.name] = fi;
@@ -57,6 +60,33 @@
         enumValues);
   }
 
+  /// Adds PbFieldType.OS String with no default value to reduce generated
+  /// code size.
+  void aOS(int tagNumber, String name) {
+    add<String>(tagNumber, name, PbFieldType.OS, null, null, null, null);
+  }
+
+  /// Adds PbFieldType.PS String with no default value.
+  void pPS(int tagNumber, String name) {
+    addRepeated<String>(tagNumber, name, PbFieldType.PS,
+        getCheckFunction(PbFieldType.PS), null, null, null);
+  }
+
+  /// Adds PbFieldType.QS String with no default value.
+  void aQS(int tagNumber, String name) {
+    add<String>(tagNumber, name, PbFieldType.QS, null, null, null, null);
+  }
+
+  /// Adds Int64 field with Int64.ZERO default.
+  void aInt64(int tagNumber, String name) {
+    add<Int64>(tagNumber, name, PbFieldType.O6, Int64.ZERO, null, null, null);
+  }
+
+  /// Adds a boolean with no default value.
+  void aOB(int tagNumber, String name) {
+    add<bool>(tagNumber, name, PbFieldType.OB, null, null, null, null);
+  }
+
   // Enum.
   void e<T>(int tagNumber, String name, int fieldType, dynamic defaultOrMaker,
       ValueOfFunc valueOf, List<ProtobufEnum> enumValues) {
diff --git a/lib/src/protobuf/field_set.dart b/lib/src/protobuf/field_set.dart
index 15e6a25..34e57c9 100644
--- a/lib/src/protobuf/field_set.dart
+++ b/lib/src/protobuf/field_set.dart
@@ -27,13 +27,17 @@
 
   _FieldSet(this._message, BuilderInfo meta, this._eventPlugin)
       : this._meta = meta,
-        _values = _makeValueList(meta.fieldInfo);
+        _values = _makeValueList(meta.byIndex.length);
 
-  static _makeValueList(Map<int, FieldInfo> infos) {
-    if (infos.isEmpty) return const [];
-    return new List(infos.length);
+  static _makeValueList(int length) {
+    if (length == 0) return _zeroList;
+    return new List(length);
   }
 
+  // Use a fixed length list and not a constant list to ensure that _values
+  // always has the same implementation type.
+  static List _zeroList = new List(0);
+
   // Metadata about multiple fields
 
   String get _messageName => _meta.messageName;
@@ -69,6 +73,9 @@
   /// Returns FieldInfo for a non-extension field, or null if not found.
   FieldInfo _nonExtensionInfo(int tagNumber) => _meta.fieldInfo[tagNumber];
 
+  /// Returns FieldInfo for a non-extension field.
+  FieldInfo _nonExtensionInfoByIndex(int index) => _meta.byIndex[index];
+
   /// Returns the FieldInfo for a regular or extension field.
   /// throws ArgumentException if no info is found.
   FieldInfo _ensureInfo(int tagNumber) {
@@ -138,7 +145,7 @@
 
   bool _hasField(int tagNumber) {
     var fi = _nonExtensionInfo(tagNumber);
-    if (fi != null) return _$has(fi.index, tagNumber);
+    if (fi != null) return _$has(fi.index);
     if (!_hasExtensions) return false;
     return _extensions._hasField(tagNumber);
   }
@@ -235,17 +242,43 @@
   // Generated method implementations
 
   /// The implementation of a generated getter.
-  T _$get<T>(int index, int tagNumber, T defaultValue) {
-    assert(_nonExtensionInfo(tagNumber).index == index);
+  T _$get<T>(int index, T defaultValue) {
     var value = _values[index];
     if (value != null) return value as T;
     if (defaultValue != null) return defaultValue;
-    return _getDefault(_nonExtensionInfo(tagNumber)) as T;
+    return _getDefault(_nonExtensionInfoByIndex(index)) as T;
   }
 
-  /// The implementation of a generated has method.
-  bool _$has(int index, int tagNumber) {
-    assert(_nonExtensionInfo(tagNumber).index == index);
+  /// The implementation of a generated getter. Common case for submessages.
+  T _$getN<T>(int index) {
+    var value = _values[index];
+    if (value != null) return value as T;
+    return _getDefault(_nonExtensionInfoByIndex(index)) as T;
+  }
+
+  /// The implementation of a generated getter for String fields.
+  String _$getS(int index, String defaultValue) {
+    var value = _values[index];
+    if (value == null) {
+      if (defaultValue != null) return defaultValue;
+      value = _getDefault(_nonExtensionInfoByIndex(index));
+    }
+    String result = value;
+    return result;
+  }
+
+  /// The implementation of a generated getter for Int64 fields.
+  Int64 _$getI64(int index) {
+    var value = _values[index];
+    if (value == null) {
+      value = _getDefault(_nonExtensionInfoByIndex(index));
+    }
+    Int64 result = value;
+    return result;
+  }
+
+  /// The implementation of a generated 'has' method.
+  bool _$has(int index) {
     var value = _values[index];
     if (value == null) return false;
     if (value is List) return value.isNotEmpty;
@@ -257,25 +290,24 @@
   /// In production, does no validation other than a null check.
   /// Only handles non-repeated, non-extension fields.
   /// Also, doesn't handle enums or messages which need per-type validation.
-  void _$set(int index, int tagNumber, value) {
-    assert(_nonExtensionInfo(tagNumber).index == index);
-    assert(!_nonExtensionInfo(tagNumber).isRepeated);
-    assert(_$check(tagNumber, value));
+  void _$set(int index, value) {
+    assert(!_nonExtensionInfoByIndex(index).isRepeated);
+    assert(_$check(index, value));
     if (_isReadOnly) {
       throw new UnsupportedError(
           "attempted to call a setter on a read-only message ($_messageName)");
     }
     if (value == null) {
-      _$check(tagNumber, value); // throw exception for null value
+      _$check(index, value); // throw exception for null value
     }
     if (_hasObservers) {
-      _eventPlugin.beforeSetField(_nonExtensionInfo(tagNumber), value);
+      _eventPlugin.beforeSetField(_nonExtensionInfoByIndex(index), value);
     }
     _values[index] = value;
   }
 
-  bool _$check(int tagNumber, var newValue) {
-    _validateField(_nonExtensionInfo(tagNumber), newValue);
+  bool _$check(int index, var newValue) {
+    _validateField(_nonExtensionInfoByIndex(index), newValue);
     return true; // Allows use in an assertion.
   }
 
@@ -486,12 +518,16 @@
 
     if (fi.isRepeated) {
       if (mustClone) {
+        // fieldValue must be a PbList of GeneratedMessage.
+        PbList<GeneratedMessage> pbList = fieldValue;
         // Copy the mapped values to a List to avoid redundant cloning (since
         // PbList.addAll iterates over its argument twice).
         _ensureRepeatedField(fi)
-            .addAll(new List.from(fieldValue.map(_cloneMessage)));
+            .addAll(new List.from(pbList.map(_cloneMessage)));
       } else {
-        _ensureRepeatedField(fi).addAll(fieldValue);
+        // fieldValue must be at least a PbList.
+        PbList pbList = fieldValue;
+        _ensureRepeatedField(fi).addAll(pbList);
       }
       return;
     }
diff --git a/lib/src/protobuf/generated_message.dart b/lib/src/protobuf/generated_message.dart
index e4657e5..338fbc5 100644
--- a/lib/src/protobuf/generated_message.dart
+++ b/lib/src/protobuf/generated_message.dart
@@ -276,56 +276,65 @@
   ///
   /// Throws an [:ArgumentError:] if [value] is [:null:]. To clear a field of
   /// it's current value, use [clearField] instead.
-  void setField(int tagNumber, value) => _fieldSet._setField(tagNumber, value);
+  void setField(int tagNumber, value) {
+    _fieldSet._setField(tagNumber, value);
+    return; // ignore: dead_code
+    return; // ignore: dead_code
+  }
 
   /// For generated code only.
-  T $_get<T>(int index, int tagNumber, T defaultValue) =>
-      _fieldSet._$get<T>(index, tagNumber, defaultValue);
+  T $_get<T>(int index, T defaultValue) =>
+      _fieldSet._$get<T>(index, defaultValue);
 
   /// For generated code only.
-  bool $_has(int index, int tagNumber) => _fieldSet._$has(index, tagNumber);
+  T $_getN<T>(int index) => _fieldSet._$getN<T>(index);
 
   /// For generated code only.
-  void $_setBool(int index, int tagNumber, bool value) =>
-      _fieldSet._$set(index, tagNumber, value);
+  String $_getS(int index, String defaultValue) =>
+      _fieldSet._$getS(index, defaultValue);
 
   /// For generated code only.
-  void $_setBytes(int index, int tagNumber, List<int> value) =>
-      _fieldSet._$set(index, tagNumber, value);
+  Int64 $_getI64(int index) => _fieldSet._$getI64(index);
 
   /// For generated code only.
-  void $_setString(int index, int tagNumber, String value) =>
-      _fieldSet._$set(index, tagNumber, value);
+  bool $_has(int index) => _fieldSet._$has(index);
 
   /// For generated code only.
-  void $_setFloat(int index, int tagNumber, double value) {
+  void $_setBool(int index, bool value) => _fieldSet._$set(index, value);
+
+  /// For generated code only.
+  void $_setBytes(int index, List<int> value) => _fieldSet._$set(index, value);
+
+  /// For generated code only.
+  void $_setString(int index, String value) => _fieldSet._$set(index, value);
+
+  /// For generated code only.
+  void $_setFloat(int index, double value) {
     if (value == null || !_isFloat32(value)) {
-      _fieldSet._$check(tagNumber, value);
+      _fieldSet._$check(index, value);
     }
-    _fieldSet._$set(index, tagNumber, value);
+    _fieldSet._$set(index, value);
   }
 
   /// For generated code only.
-  void $_setDouble(int index, int tagNumber, double value) =>
-      _fieldSet._$set(index, tagNumber, value);
+  void $_setDouble(int index, double value) => _fieldSet._$set(index, value);
 
   /// For generated code only.
-  void $_setSignedInt32(int index, int tagNumber, int value) {
+  void $_setSignedInt32(int index, int value) {
     if (value == null || !_isSigned32(value)) {
-      _fieldSet._$check(tagNumber, value);
+      _fieldSet._$check(index, value);
     }
-    _fieldSet._$set(index, tagNumber, value);
+    _fieldSet._$set(index, value);
   }
 
   /// For generated code only.
-  void $_setUnsignedInt32(int index, int tagNumber, int value) {
+  void $_setUnsignedInt32(int index, int value) {
     if (value == null || !_isUnsigned32(value)) {
-      _fieldSet._$check(tagNumber, value);
+      _fieldSet._$check(index, value);
     }
-    _fieldSet._$set(index, tagNumber, value);
+    _fieldSet._$set(index, value);
   }
 
   /// For generated code only.
-  void $_setInt64(int index, int tagNumber, Int64 value) =>
-      _fieldSet._$set(index, tagNumber, value);
+  void $_setInt64(int index, Int64 value) => _fieldSet._$set(index, value);
 }
diff --git a/lib/src/protobuf/json.dart b/lib/src/protobuf/json.dart
index 6c3afb1..62d276e 100644
--- a/lib/src/protobuf/json.dart
+++ b/lib/src/protobuf/json.dart
@@ -5,11 +5,12 @@
 part of protobuf;
 
 Map<String, dynamic> _writeToJsonMap(_FieldSet fs) {
-  convertToMap(fieldValue, fieldType) {
+  convertToMap(fieldValue, int fieldType) {
     int baseType = PbFieldType._baseType(fieldType);
 
     if (_isRepeated(fieldType)) {
-      return new List.from(fieldValue.map((e) => convertToMap(e, baseType)));
+      PbList pbList = fieldValue;
+      return new List.from(pbList.map((e) => convertToMap(e, baseType)));
     }
 
     switch (baseType) {
@@ -67,8 +68,10 @@
 // (Called recursively on nested messages.)
 void _mergeFromJsonMap(
     _FieldSet fs, Map<String, dynamic> json, ExtensionRegistry registry) {
-  for (String key in json.keys) {
-    var fi = fs._meta.byTagAsString[key];
+  Iterable<String> keys = json.keys;
+  var meta = fs._meta;
+  for (String key in keys) {
+    var fi = meta.byTagAsString[key];
     if (fi == null) {
       if (registry == null) continue; // Unknown tag; skip
       fi = registry.getExtension(fs._messageName, int.parse(key));
@@ -83,11 +86,14 @@
 }
 
 void _appendJsonList(
-    _FieldSet fs, List json, FieldInfo fi, ExtensionRegistry registry) {
+    _FieldSet fs, List jsonList, FieldInfo fi, ExtensionRegistry registry) {
   List repeated = fs._ensureRepeatedField(fi);
-  var length = json.length;
-  for (int i = 0; i < length; ++i) {
-    var value = json[i];
+  // Micro optimization. Using "for in" generates the following and iterator
+  // alloc:
+  //   for (t1 = J.get$iterator$ax(json), t2 = fi.tagNumber, t3 = fi.type,
+  //       t4 = J.getInterceptor$ax(repeated); t1.moveNext$0();)
+  for (int i = 0, len = jsonList.length; i < len; i++) {
+    var value = jsonList[i];
     var convertedValue =
         _convertJsonValue(fs, value, fi.tagNumber, fi.type, registry);
     if (convertedValue != null) {
@@ -99,10 +105,15 @@
 void _setJsonField(
     _FieldSet fs, json, FieldInfo fi, ExtensionRegistry registry) {
   var value = _convertJsonValue(fs, json, fi.tagNumber, fi.type, registry);
-  if (value != null) {
+  if (value == null) return;
+  // _convertJsonValue throws exception when it fails to do conversion.
+  // Therefore we run _validateField for debug builds only to validate
+  // correctness of conversion.
+  assert(() {
     fs._validateField(fi, value);
-    fs._setFieldUnchecked(fi, value);
-  }
+    return true;
+  }());
+  fs._setFieldUnchecked(fi, value);
 }
 
 /// Converts [value] from the Json format to the Dart data type
@@ -185,16 +196,16 @@
     case PbFieldType._FIXED64_BIT:
     case PbFieldType._SFIXED64_BIT:
       if (value is int) return new Int64(value);
-      if (value is String) return Int64.parseRadix(value, 10);
+      if (value is String) return Int64.parseInt(value);
       expectedType = 'int or stringified int';
       break;
     case PbFieldType._GROUP_BIT:
     case PbFieldType._MESSAGE_BIT:
       if (value is Map) {
+        Map<String, dynamic> messageValue = value;
         GeneratedMessage subMessage =
             fs._meta._makeEmptyMessage(tagNumber, registry);
-        _mergeFromJsonMap(
-            subMessage._fieldSet, value as Map<String, dynamic>, registry);
+        _mergeFromJsonMap(subMessage._fieldSet, messageValue, registry);
         return subMessage;
       }
       expectedType = 'nested message or group';
diff --git a/lib/src/protobuf/mixins/map_mixin.dart b/lib/src/protobuf/mixins/map_mixin.dart
index 0032552..7079aa4 100644
--- a/lib/src/protobuf/mixins/map_mixin.dart
+++ b/lib/src/protobuf/mixins/map_mixin.dart
@@ -6,12 +6,13 @@
 
 import "package:protobuf/protobuf.dart" show BuilderInfo;
 
-/// A PbMapMixin provides an experimental implementation of the
-/// Map interface for a GeneratedMessage.
+/// Note that this class does not claim to implement [Map]. Instead, this needs
+/// to be specified using a dart_options.imports clause specifying MapMixin as a
+/// parent mixin to PbMapMixin.
 ///
-/// This mixin is enabled via an option in
-/// dart_options.proto in dart-protoc-plugin.
-abstract class PbMapMixin implements Map {
+/// Since PbMapMixin is built in, this is done automatically, so this mixin can
+/// be enabled by specifying only a dart_options.mixin option.
+abstract class PbMapMixin {
   // GeneratedMessage properties and methods used by this mixin.
 
   BuilderInfo get info_;
@@ -20,7 +21,6 @@
   getField(int tagNumber);
   void setField(int tagNumber, var value);
 
-  @override
   operator [](key) {
     if (key is! String) return null;
     var tag = getTagNumber(key);
@@ -28,7 +28,6 @@
     return getField(tag);
   }
 
-  @override
   operator []=(key, val) {
     var tag = getTagNumber(key as String);
     if (tag == null) {
@@ -38,16 +37,12 @@
     setField(tag, val);
   }
 
-  @override
-  get keys => info_.byName.keys;
+  Iterable<String> get keys => info_.byName.keys;
 
-  @override
   bool containsKey(Object key) => info_.byName.containsKey(key);
 
-  @override
-  get length => info_.byName.length;
+  int get length => info_.byName.length;
 
-  @override
   remove(key) {
     throw new UnsupportedError(
         "remove() not supported by ${info_.messageName}");
diff --git a/lib/src/protobuf/pb_list.dart b/lib/src/protobuf/pb_list.dart
index 9176a82..85f7025 100644
--- a/lib/src/protobuf/pb_list.dart
+++ b/lib/src/protobuf/pb_list.dart
@@ -6,7 +6,7 @@
 
 typedef void CheckFunc<E>(E x);
 
-class PbList<E> extends Object with ListMixin<E> implements List<E> {
+class PbList<E> extends ListBase<E> {
   final List<E> _wrappedList;
   final CheckFunc<E> check;
 
@@ -44,11 +44,93 @@
   /// `f` on each element of this `PbList` in iteration order.
   Iterable<T> map<T>(T f(E e)) => _wrappedList.map<T>(f);
 
+  /// Returns a new lazy [Iterable] with all elements that satisfy the predicate
+  /// [test].
+  Iterable<E> where(bool test(E element)) => _wrappedList.where(test);
+
+  /// Expands each element of this [Iterable] into zero or more elements.
+  Iterable<T> expand<T>(Iterable<T> f(E element)) => _wrappedList.expand(f);
+
+  /// Returns true if the collection contains an element equal to [element].
+  bool contains(Object element) => _wrappedList.contains(element);
+
   /// Applies the function [f] to each element of this list in iteration order.
   void forEach(void f(E element)) {
     _wrappedList.forEach(f);
   }
 
+  /// Reduces a collection to a single value by iteratively combining elements
+  /// of the collection using the provided function.
+  E reduce(E combine(E value, E element)) => _wrappedList.reduce(combine);
+
+  /// Reduces a collection to a single value by iteratively combining each
+  /// element of the collection with an existing value.
+  T fold<T>(T initialValue, T combine(dynamic previousValue, E element)) =>
+      _wrappedList.fold(initialValue, combine);
+
+  /// Checks whether every element of this iterable satisfies [test].
+  bool every(bool test(E element)) => _wrappedList.every(test);
+
+  /// Converts each element to a [String] and concatenates the strings.
+  String join([String separator = ""]) => _wrappedList.join(separator);
+
+  /// Checks whether any element of this iterable satisfies [test].
+  bool any(bool test(E element)) => _wrappedList.any(test);
+
+  /// Creates a [List] containing the elements of this [Iterable].
+  List<E> toList({bool growable: true}) =>
+      _wrappedList.toList(growable: growable);
+
+  /// Creates a [Set] containing the same elements as this iterable.
+  Set<E> toSet() => _wrappedList.toSet();
+
+  /// Returns `true` if there are no elements in this collection.
+  bool get isEmpty => _wrappedList.isEmpty;
+
+  /// Returns `true` if there is at least one element in this collection.
+  bool get isNotEmpty => _wrappedList.isNotEmpty;
+
+  /// Returns a lazy iterable of the [count] first elements of this iterable.
+  Iterable<E> take(int count) => _wrappedList.take(count);
+
+  /// Returns a lazy iterable of the leading elements satisfying [test].
+  Iterable<E> takeWhile(bool test(E value)) => _wrappedList.takeWhile(test);
+
+  /// Returns an [Iterable] that provides all but the first [count] elements.
+  Iterable<E> skip(int count) => _wrappedList.skip(count);
+
+  /// Returns an `Iterable` that skips leading elements while [test] is
+  /// satisfied.
+  Iterable<E> skipWhile(bool test(E value)) => _wrappedList.skipWhile(test);
+
+  /// Returns the first element.
+  E get first => _wrappedList.first;
+
+  /// Returns the last element.
+  E get last => _wrappedList.last;
+
+  /// Checks that this iterable has only one element, and returns that element.
+  E get single => _wrappedList.single;
+
+  /// Returns the first element that satisfies the given predicate [test].
+  E firstWhere(bool test(E element), {E orElse()}) =>
+      _wrappedList.firstWhere(test, orElse: orElse);
+
+  /// Returns the last element that satisfies the given predicate [test].
+  E lastWhere(bool test(E element), {E orElse()}) =>
+      _wrappedList.lastWhere(test, orElse: orElse);
+
+  /// Returns the single element that satisfies [test].
+  // TODO(jakobr): Implement once Dart 2 corelib changes have landed.
+  //E singleWhere(bool test(E element), {E orElse()}) =>
+  //    _wrappedList.singleWhere(test, orElse: orElse);
+
+  /// Returns the [index]th element.
+  E elementAt(int index) => _wrappedList.elementAt(index);
+
+  /// Returns a string representation of (some of) the elements of `this`.
+  String toString() => _wrappedList.toString();
+
   /// Returns the element at the given [index] in the list or throws an
   /// [IndexOutOfRangeException] if [index] is out of bounds.
   E operator [](int index) => _wrappedList[index];
@@ -60,14 +142,17 @@
     _wrappedList[index] = value;
   }
 
+  /// Returns the number of elements in this collection.
+  int get length => _wrappedList.length;
+
   /// Unsupported -- violated non-null constraint imposed by protobufs.
   ///
   /// Changes the length of the list. If [newLength] is greater than the current
   /// [length], entries are initialized to [:null:]. Throws an
   /// [UnsupportedError] if the list is not extendable.
-  void set length(int newLength) {
+  set length(int newLength) {
     if (newLength > length) {
-      throw new ArgumentError('Extending protobuf lists is not supported');
+      throw new UnsupportedError('Extending protobuf lists is not supported');
     }
     _wrappedList.length = newLength;
   }
@@ -87,15 +172,28 @@
     _wrappedList.addAll(collection);
   }
 
-  /// Copies [:end - start:] elements of the [from] array, starting from
-  /// [skipCount], into [:this:], starting at [start].
-  /// Throws an [UnsupportedError] if the list is not extendable.
-  void setRange(int start, int end, Iterable<E> from, [int skipCount = 0]) {
-    // NOTE: In case `take()` returns less than `end - start` elements, the
-    // _wrappedList will fail with a `StateError`.
-    from.skip(skipCount).take(end - start).forEach(_validate);
-    _wrappedList.setRange(start, end, from, skipCount);
-  }
+  /// Returns an [Iterable] of the objects in this list in reverse order.
+  Iterable<E> get reversed => _wrappedList.reversed;
+
+  /// Sorts this list according to the order specified by the [compare]
+  /// function.
+  void sort([int compare(E a, E b)]) => _wrappedList.sort(compare);
+
+  /// Shuffles the elements of this list randomly.
+  void shuffle([math.Random random]) => _wrappedList.shuffle(random);
+
+  // TODO(jakobr): E instead of Object once dart-lang/sdk#31311 is fixed.
+  /// Returns the first index of [element] in this list.
+  int indexOf(Object element, [int start = 0]) =>
+      _wrappedList.indexOf(element, start);
+
+  // TODO(jakobr): E instead of Object once dart-lang/sdk#31311 is fixed.
+  /// Returns the last index of [element] in this list.
+  int lastIndexOf(Object element, [int start]) =>
+      _wrappedList.lastIndexOf(element, start);
+
+  /// Removes all objects from this list; the length of the list becomes zero.
+  void clear() => _wrappedList.clear();
 
   /// Inserts a new element in the list.
   /// The element must be valid (and not nullable) for the PbList type.
@@ -121,8 +219,59 @@
     _wrappedList.setAll(index, iterable);
   }
 
-  /// Returns the number of elements in this collection.
-  int get length => _wrappedList.length;
+  /// Removes the first occurrence of [value] from this list.
+  bool remove(Object value) => _wrappedList.remove(value);
+
+  /// Removes the object at position [index] from this list.
+  E removeAt(int index) => _wrappedList.removeAt(index);
+
+  /// Pops and returns the last object in this list.
+  E removeLast() => _wrappedList.removeLast();
+
+  /// Removes all objects from this list that satisfy [test].
+  void removeWhere(bool test(E element)) => _wrappedList.removeWhere(test);
+
+  /// Removes all objects from this list that fail to satisfy [test].
+  void retainWhere(bool test(E element)) => _wrappedList.retainWhere(test);
+
+  /// Returns a new list containing the objects from [start] inclusive to [end]
+  /// exclusive.
+  List<E> sublist(int start, [int end]) => _wrappedList.sublist(start, end);
+
+  /// Returns an [Iterable] that iterates over the objects in the range [start]
+  /// inclusive to [end] exclusive.
+  Iterable<E> getRange(int start, int end) => _wrappedList.getRange(start, end);
+
+  /// Copies [:end - start:] elements of the [from] array, starting from
+  /// [skipCount], into [:this:], starting at [start].
+  /// Throws an [UnsupportedError] if the list is not extendable.
+  void setRange(int start, int end, Iterable<E> from, [int skipCount = 0]) {
+    // NOTE: In case `take()` returns less than `end - start` elements, the
+    // _wrappedList will fail with a `StateError`.
+    from.skip(skipCount).take(end - start).forEach(_validate);
+    _wrappedList.setRange(start, end, from, skipCount);
+  }
+
+  /// Removes the objects in the range [start] inclusive to [end] exclusive.
+  void removeRange(int start, int end) => _wrappedList.removeRange(start, end);
+
+  /// Sets the objects in the range [start] inclusive to [end] exclusive to the
+  /// given [fillValue].
+  void fillRange(int start, int end, [E fillValue]) {
+    _validate(fillValue);
+    _wrappedList.fillRange(start, end, fillValue);
+  }
+
+  /// Removes the objects in the range [start] inclusive to [end] exclusive and
+  /// inserts the contents of [replacement] in its place.
+  void replaceRange(int start, int end, Iterable<E> replacement) {
+    final values = replacement.toList();
+    replacement.forEach(_validate);
+    _wrappedList.replaceRange(start, end, values);
+  }
+
+  /// Returns an unmodifiable [Map] view of `this`.
+  Map<int, E> asMap() => _wrappedList.asMap();
 
   void _validate(E val) {
     check(val);
diff --git a/pubspec.yaml b/pubspec.yaml
index 3b2adb9..32a55c8 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
 name: protobuf
-version: 0.6.0
+version: 0.6.1
 author: Dart Team <misc@dartlang.org>
 description: Runtime library for protocol buffers support.
 homepage: https://github.com/dart-lang/protobuf
diff --git a/test/mock_util.dart b/test/mock_util.dart
index b67bf8d..8b58b92 100644
--- a/test/mock_util.dart
+++ b/test/mock_util.dart
@@ -22,18 +22,18 @@
   // subclasses must provide these
   BuilderInfo get info_;
 
-  int get val => $_get(0, 1, 42);
+  int get val => $_get(0, 42);
   set val(x) => setField(1, x);
 
-  String get str => $_get(1, 2, "");
-  set str(x) => $_setString(1, 2, x);
+  String get str => $_getS(1, "");
+  set str(x) => $_setString(1, x);
 
-  MockMessage get child => $_get(2, 3, null);
+  MockMessage get child => $_getN(2);
   set child(x) => setField(3, x);
 
-  List<int> get int32s => $_get(3, 4, null);
+  List<int> get int32s => $_getN(3);
 
-  Int64 get int64 => $_get(4, 5, new Int64(0));
+  Int64 get int64 => $_get(4, new Int64(0));
   set int64(x) => setField(5, x);
 
   clone() {