Introduce protoName on fields (#273)

diff --git a/protobuf/CHANGELOG.md b/protobuf/CHANGELOG.md
index d446021..fb1c577 100644
--- a/protobuf/CHANGELOG.md
+++ b/protobuf/CHANGELOG.md
@@ -1,3 +1,11 @@
+## 0.14.0
+
+* `FieldInfo` objects have a new getter `.protoName` that gives the non-camel-case name of the field
+  as in the `.proto`-file.
+* **Breaking**: The field-adder methods on `BuilderInfo` now takes only named optional arguments.
+  To migrate, update `protoc_plugin` to version 18.0.0 or higher.
+* The field-adder methods on `BuilderInfo` all take a new argument `protoName`.
+
 ## 0.13.16
 
 * Better handling of dummy calls to `BuilderInfo.add` with a tag number of 0.
diff --git a/protobuf/lib/src/protobuf/builder_info.dart b/protobuf/lib/src/protobuf/builder_info.dart
index 7de6062..98bb04e 100644
--- a/protobuf/lib/src/protobuf/builder_info.dart
+++ b/protobuf/lib/src/protobuf/builder_info.dart
@@ -28,20 +28,27 @@
       dynamic defaultOrMaker,
       CreateBuilderFunc subBuilder,
       ValueOfFunc valueOf,
-      List<ProtobufEnum> enumValues) {
+      List<ProtobufEnum> enumValues,
+      {String protoName}) {
     var index = byIndex.length;
     final fieldInfo = (tagNumber == 0)
         ? FieldInfo.dummy(index)
-        : FieldInfo<T>(name, tagNumber, index, fieldType, defaultOrMaker,
-            subBuilder, valueOf, enumValues);
+        : FieldInfo<T>(name, tagNumber, index, fieldType,
+            defaultOrMaker: defaultOrMaker,
+            subBuilder: subBuilder,
+            valueOf: valueOf,
+            enumValues: enumValues,
+            protoName: protoName);
     _addField(fieldInfo);
   }
 
   void addMapField<K, V>(int tagNumber, String name, int keyFieldType,
-      int valueFieldType, BuilderInfo mapEntryBuilderInfo) {
+      int valueFieldType, BuilderInfo mapEntryBuilderInfo,
+      {String protoName}) {
     var index = byIndex.length;
-    _addField(MapFieldInfo<K, V>.map(name, tagNumber, index, PbFieldType.M,
-        keyFieldType, valueFieldType, mapEntryBuilderInfo));
+    _addField(MapFieldInfo<K, V>(name, tagNumber, index, PbFieldType.M,
+        keyFieldType, valueFieldType, mapEntryBuilderInfo,
+        protoName: protoName));
   }
 
   void addRepeated<T>(
@@ -51,10 +58,12 @@
       CheckFunc<T> check,
       CreateBuilderFunc subBuilder,
       ValueOfFunc valueOf,
-      List<ProtobufEnum> enumValues) {
+      List<ProtobufEnum> enumValues,
+      {String protoName}) {
     var index = byIndex.length;
-    _addField(FieldInfo<T>.repeated(name, tagNumber, index, fieldType, check,
-        subBuilder, valueOf, enumValues));
+    _addField(FieldInfo<T>.repeated(
+        name, tagNumber, index, fieldType, check, subBuilder,
+        valueOf: valueOf, enumValues: enumValues, protoName: protoName));
   }
 
   void _addField(FieldInfo fi) {
@@ -71,50 +80,54 @@
   }
 
   void a<T>(int tagNumber, String name, int fieldType,
-      [dynamic defaultOrMaker,
+      {dynamic defaultOrMaker,
       CreateBuilderFunc subBuilder,
       ValueOfFunc valueOf,
-      List<ProtobufEnum> enumValues]) {
+      List<ProtobufEnum> enumValues,
+      String protoName}) {
     add<T>(tagNumber, name, fieldType, defaultOrMaker, subBuilder, valueOf,
         enumValues);
   }
 
   /// Adds PbFieldType.OS String with no default value to reduce generated
   /// code size.
-  void aOS(int tagNumber, String name) {
+  void aOS(int tagNumber, String name, {String protoName}) {
     add<String>(tagNumber, name, PbFieldType.OS, null, null, null, null);
   }
 
   /// Adds PbFieldType.PS String with no default value.
-  void pPS(int tagNumber, String name) {
+  void pPS(int tagNumber, String name, {String protoName}) {
     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) {
+  void aQS(int tagNumber, String name, {String protoName}) {
     add<String>(tagNumber, name, PbFieldType.QS, null, null, null, null);
   }
 
   /// Adds Int64 field with Int64.ZERO default.
-  void aInt64(int tagNumber, String name) {
+  void aInt64(int tagNumber, String name, {String protoName}) {
     add<Int64>(tagNumber, name, PbFieldType.O6, Int64.ZERO, null, null, null);
   }
 
   /// Adds a boolean with no default value.
-  void aOB(int tagNumber, String name) {
+  void aOB(int tagNumber, String name, {String protoName}) {
     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) {
+  void e<T>(int tagNumber, String name, int fieldType,
+      {dynamic defaultOrMaker,
+      ValueOfFunc valueOf,
+      List<ProtobufEnum> enumValues,
+      String protoName}) {
     add<T>(
         tagNumber, name, fieldType, defaultOrMaker, null, valueOf, enumValues);
   }
 
   // Repeated, not a message, group, or enum.
-  void p<T>(int tagNumber, String name, int fieldType) {
+  void p<T>(int tagNumber, String name, int fieldType, {String protoName}) {
     assert(!_isGroupOrMessage(fieldType) && !_isEnum(fieldType));
     addRepeated<T>(tagNumber, name, fieldType, getCheckFunction(fieldType),
         null, null, null);
@@ -122,20 +135,10 @@
 
   // Repeated message, group, or enum.
   void pc<T>(int tagNumber, String name, int fieldType,
-      [CreateBuilderFunc subBuilder,
+      {CreateBuilderFunc subBuilder,
       ValueOfFunc valueOf,
-      List<ProtobufEnum> enumValues]) {
-    assert(_isGroupOrMessage(fieldType) || _isEnum(fieldType));
-    addRepeated<T>(tagNumber, name, fieldType, _checkNotNull, subBuilder,
-        valueOf, enumValues);
-  }
-
-  @Deprecated('Use [pc] instead. The given [check] function is ignored.'
-      'This function will be removed in the next major version.')
-  void pp<T>(int tagNumber, String name, int fieldType, CheckFunc<T> check,
-      [CreateBuilderFunc subBuilder,
-      ValueOfFunc valueOf,
-      List<ProtobufEnum> enumValues]) {
+      List<ProtobufEnum> enumValues,
+      String protoName}) {
     assert(_isGroupOrMessage(fieldType) || _isEnum(fieldType));
     addRepeated<T>(tagNumber, name, fieldType, _checkNotNull, subBuilder,
         valueOf, enumValues);
@@ -147,12 +150,15 @@
   }
 
   // Map field.
-  void m<K, V>(int tagNumber, String name, String entryClassName,
-      int keyFieldType, int valueFieldType,
-      [CreateBuilderFunc valueCreator,
+  void m<K, V>(int tagNumber, String name,
+      {String entryClassName,
+      int keyFieldType,
+      int valueFieldType,
+      CreateBuilderFunc valueCreator,
       ValueOfFunc valueOf,
       List<ProtobufEnum> enumValues,
-      PackageName packageName = const PackageName('')]) {
+      PackageName packageName = const PackageName(''),
+      String protoName}) {
     BuilderInfo mapEntryBuilderInfo = BuilderInfo(entryClassName,
         package: packageName)
       ..add(PbMap._keyFieldNumber, 'key', keyFieldType, null, null, null, null)
@@ -160,7 +166,8 @@
           valueCreator, valueOf, enumValues);
 
     addMapField<K, V>(
-        tagNumber, name, keyFieldType, valueFieldType, mapEntryBuilderInfo);
+        tagNumber, name, keyFieldType, valueFieldType, mapEntryBuilderInfo,
+        protoName: protoName);
   }
 
   bool containsTagNumber(int tagNumber) => fieldInfo.containsKey(tagNumber);
diff --git a/protobuf/lib/src/protobuf/extension.dart b/protobuf/lib/src/protobuf/extension.dart
index 59e350f..2d87f79 100644
--- a/protobuf/lib/src/protobuf/extension.dart
+++ b/protobuf/lib/src/protobuf/extension.dart
@@ -9,20 +9,26 @@
   final String extendee;
 
   Extension(this.extendee, String name, int tagNumber, int fieldType,
-      [dynamic defaultOrMaker,
+      {dynamic defaultOrMaker,
       CreateBuilderFunc subBuilder,
       ValueOfFunc valueOf,
-      List<ProtobufEnum> enumValues])
-      : super(name, tagNumber, null, fieldType, defaultOrMaker, subBuilder,
-            valueOf, enumValues);
+      List<ProtobufEnum> enumValues,
+      String protoName})
+      : super(name, tagNumber, null, fieldType,
+            defaultOrMaker: defaultOrMaker,
+            subBuilder: subBuilder,
+            valueOf: valueOf,
+            enumValues: enumValues,
+            protoName: protoName);
 
   Extension.repeated(this.extendee, String name, int tagNumber, int fieldType,
-      CheckFunc<T> check,
-      [CreateBuilderFunc subBuilder,
+      {CheckFunc<T> check,
+      CreateBuilderFunc subBuilder,
       ValueOfFunc valueOf,
-      List<ProtobufEnum> enumValues])
+      List<ProtobufEnum> enumValues,
+      String protoName})
       : super.repeated(name, tagNumber, null, fieldType, check, subBuilder,
-            valueOf, enumValues);
+            valueOf: valueOf, enumValues: enumValues, protoName: protoName);
 
   int get hashCode => extendee.hashCode * 31 + tagNumber;
 
diff --git a/protobuf/lib/src/protobuf/field_info.dart b/protobuf/lib/src/protobuf/field_info.dart
index 70e9f8c..65ec162 100644
--- a/protobuf/lib/src/protobuf/field_info.dart
+++ b/protobuf/lib/src/protobuf/field_info.dart
@@ -8,10 +8,13 @@
 class FieldInfo<T> {
   FrozenPbList<T> _emptyList;
 
-  // BuilderInfo used when creating a field set for a map field.
-  final BuilderInfo _mapEntryBuilderInfo;
-
   final String name;
+
+  /// The name of this field as written in the proto-definition.
+  ///
+  /// This will typically consist of words separated with underscores.
+  final String protoName;
+
   final int tagNumber;
   final int index; // index of the field's value. Null for extensions.
   final int type;
@@ -38,35 +41,38 @@
   // (Not used for non-repeated fields.)
   final CheckFunc<T> check;
 
-  FieldInfo(this.name, this.tagNumber, this.index, int type,
-      [dynamic defaultOrMaker, this.subBuilder, this.valueOf, this.enumValues])
-      : this.type = type,
-        this.makeDefault = findMakeDefault(type, defaultOrMaker),
-        this.check = null,
-        _mapEntryBuilderInfo = null {
-    assert(type != 0);
-    assert(!_isGroupOrMessage(type) || subBuilder != null);
-    assert(!_isEnum(type) || valueOf != null);
-  }
+  FieldInfo(this.name, this.tagNumber, this.index, this.type,
+      {dynamic defaultOrMaker,
+      this.subBuilder,
+      this.valueOf,
+      this.enumValues,
+      String protoName})
+      : makeDefault = findMakeDefault(type, defaultOrMaker),
+        check = null,
+        protoName = protoName ?? _unCamelCase(name),
+        assert(type != 0),
+        assert(!_isGroupOrMessage(type) ||
+            subBuilder != null ||
+            _isMapField(type)),
+        assert(!_isEnum(type) || valueOf != null);
 
   // Represents a field that has been removed by a program transformation.
   FieldInfo.dummy(this.index)
       : name = '<removed field>',
+        protoName = '<removed field>',
         tagNumber = 0,
         type = 0,
         makeDefault = null,
         valueOf = null,
         check = null,
         enumValues = null,
-        subBuilder = null,
-        _mapEntryBuilderInfo = null;
+        subBuilder = null;
 
-  FieldInfo.repeated(this.name, this.tagNumber, this.index, int type,
+  FieldInfo.repeated(this.name, this.tagNumber, this.index, this.type,
       this.check, this.subBuilder,
-      [this.valueOf, this.enumValues])
-      : this.type = type,
-        this.makeDefault = (() => PbList<T>(check: check)),
-        _mapEntryBuilderInfo = null {
+      {this.valueOf, this.enumValues, String protoName})
+      : makeDefault = (() => PbList<T>(check: check)),
+        protoName = protoName ?? _unCamelCase(name) {
     assert(name != null);
     assert(tagNumber != null);
     assert(_isRepeated(type));
@@ -74,20 +80,6 @@
     assert(!_isEnum(type) || valueOf != null);
   }
 
-  FieldInfo._map(
-      this.name, this.tagNumber, this.index, int type, this.makeDefault,
-      [dynamic defaultOrMaker,
-      this.subBuilder,
-      this.valueOf,
-      this.enumValues,
-      this._mapEntryBuilderInfo])
-      : this.type = type,
-        this.check = null {
-    assert(name != null);
-    assert(tagNumber != null);
-    assert(_isMapField(type));
-  }
-
   static MakeDefaultFunc findMakeDefault(int type, dynamic defaultOrMaker) {
     if (defaultOrMaker == null) return PbFieldType._defaultForType(type);
     if (defaultOrMaker is MakeDefaultFunc) return defaultOrMaker;
@@ -188,25 +180,28 @@
   String toString() => name;
 }
 
+final RegExp _upperCase = RegExp('[A-Z]');
+
+String _unCamelCase(String name) {
+  return name.replaceAllMapped(
+      _upperCase, (match) => '_${match.group(0).toLowerCase()}');
+}
+
 class MapFieldInfo<K, V> extends FieldInfo<PbMap<K, V>> {
   int keyFieldType;
   int valueFieldType;
   CreateBuilderFunc valueCreator;
 
-  MapFieldInfo.map(String name, int tagNumber, int index, int type,
-      this.keyFieldType, this.valueFieldType, BuilderInfo mapEntryBuilderInfo)
-      : super._map(
-            name,
-            tagNumber,
-            index,
-            type,
-            () =>
-                PbMap<K, V>(keyFieldType, valueFieldType, mapEntryBuilderInfo),
-            null,
-            null,
-            null,
-            null,
-            mapEntryBuilderInfo) {
+  // BuilderInfo used when creating a field set for a map field.
+  final BuilderInfo _mapEntryBuilderInfo;
+
+  MapFieldInfo(String name, int tagNumber, int index, int type,
+      this.keyFieldType, this.valueFieldType, this._mapEntryBuilderInfo,
+      {String protoName})
+      : super(name, tagNumber, index, type,
+            defaultOrMaker: () =>
+                PbMap<K, V>(keyFieldType, valueFieldType, _mapEntryBuilderInfo),
+            protoName: protoName) {
     assert(name != null);
     assert(tagNumber != null);
     assert(_isMapField(type));
diff --git a/protobuf/pubspec.yaml b/protobuf/pubspec.yaml
index 0b384bc..0f5437b 100644
--- a/protobuf/pubspec.yaml
+++ b/protobuf/pubspec.yaml
@@ -1,5 +1,5 @@
 name: protobuf
-version: 0.13.16
+version: 0.14.0
 author: Dart Team <misc@dartlang.org>
 description: >
   Runtime library for protocol buffers support.
diff --git a/protobuf/test/mock_util.dart b/protobuf/test/mock_util.dart
index c413de5..8b4025a 100644
--- a/protobuf/test/mock_util.dart
+++ b/protobuf/test/mock_util.dart
@@ -10,9 +10,9 @@
 
 BuilderInfo mockInfo(String className, CreateBuilderFunc create) {
   return BuilderInfo(className)
-    ..a(1, "val", PbFieldType.O3, 42)
+    ..a(1, "val", PbFieldType.O3, defaultOrMaker: 42)
     ..a(2, "str", PbFieldType.OS)
-    ..a(3, "child", PbFieldType.OM, create, create)
+    ..a(3, "child", PbFieldType.OM, defaultOrMaker: create, subBuilder: create)
     ..p<int>(4, "int32s", PbFieldType.P3)
     ..a(5, "int64", PbFieldType.O6);
 }
diff --git a/protobuf/test/readonly_message_test.dart b/protobuf/test/readonly_message_test.dart
index fe1eb24..81cdb42 100644
--- a/protobuf/test/readonly_message_test.dart
+++ b/protobuf/test/readonly_message_test.dart
@@ -31,7 +31,7 @@
   @override
   BuilderInfo info_ = BuilderInfo('rec')
     ..a(1, 'value', PbFieldType.O3)
-    ..pc<Rec>(2, 'sub', PbFieldType.PM, Rec.create)
+    ..pc<Rec>(2, 'sub', PbFieldType.PM, subBuilder: Rec.create)
     ..p<int>(10, 'ints', PbFieldType.P3);
 
   int get value => $_get(0, 0);
diff --git a/protoc_plugin/CHANGELOG.md b/protoc_plugin/CHANGELOG.md
index bf260df..a99c0db 100644
--- a/protoc_plugin/CHANGELOG.md
+++ b/protoc_plugin/CHANGELOG.md
@@ -1,7 +1,10 @@
-## 18.0.0-dev
+## 18.0.0
+
+* Breaking: Generates code that requires at least `protobuf` 0.14.0.
+* Generate the non-camel-case name of fields when it cannot be derived from the json name.
 
 * Breaking: Use the correct proto3 Json CamelCase names for the string representation of field
-  names, instead of using the name of the dart identifier for that field.
+  names (also for extensions), instead of using the name of the dart identifier for that field.
 
   In most cases this name coincides with the name have emitted until now and require no change.
 
@@ -14,7 +17,8 @@
   `GeneratedMessage.getTagNumber(String FieldName)`, and calls to name-related methods of
   `GeneratedMessage._info`.
 
-  `GeneratedMessage.toString()` also uses the string representation.
+  `GeneratedMessage.toString()` also uses the string representation. It will now print the
+  json-name.
 
 ## 17.0.5
 
diff --git a/protoc_plugin/lib/extension_generator.dart b/protoc_plugin/lib/extension_generator.dart
index 6a9e36f..2e30f3e 100644
--- a/protoc_plugin/lib/extension_generator.dart
+++ b/protoc_plugin/lib/extension_generator.dart
@@ -97,55 +97,45 @@
     String name = _extensionName;
     var type = _field.baseType;
     var dartType = type.getDartType(fileGen);
+    String invocation;
+    List<String> positionals = <String>[];
+    positionals.add("'$_extendedFullName'");
+    positionals.add("'${_descriptor.jsonName}'");
+    positionals.add('${_field.number}');
+    positionals.add(_field.typeConstant);
 
+    Map<String, String> named = <String, String>{};
+    named['protoName'] = _field.quotedProtoName;
     if (_field.isRepeated) {
-      out.printAnnotated(
-          'static final $_protobufImportPrefix.Extension $name = '
-          '$_protobufImportPrefix.Extension<$dartType>.repeated(\'$_extendedFullName\','
-          ' \'$name\', ${_field.number}, ${_field.typeConstant}',
-          [
-            NamedLocation(
-                name: name,
-                fieldPathSegment: List.from(fieldPath),
-                start: 'static final $_protobufImportPrefix.Extension '.length)
-          ]);
+      invocation = '$_protobufImportPrefix.Extension<$dartType>.repeated';
+      named['check'] =
+          '$_protobufImportPrefix.getCheckFunction(${_field.typeConstant})';
       if (type.isMessage || type.isGroup) {
-        out.println(
-            ', $_protobufImportPrefix.getCheckFunction(${_field.typeConstant}), $dartType.create);');
+        named['subBuilder'] = '$dartType.create';
       } else if (type.isEnum) {
-        out.println(
-            ', $_protobufImportPrefix.getCheckFunction(${_field.typeConstant}), null, '
-            '$dartType.valueOf, $dartType.values);');
-      } else {
-        out.println(
-            ", $_protobufImportPrefix.getCheckFunction(${_field.typeConstant}));");
+        named['valueOf'] = '$dartType.valueOf';
+        named['enumValues'] = '$dartType.values';
       }
-      return;
+    } else {
+      invocation = '$_protobufImportPrefix.Extension<$dartType>';
+      named['defaultOrMaker'] = _field.generateDefaultFunction(fileGen);
+      if (type.isMessage || type.isGroup) {
+        named['subBuilder'] = '$dartType.create';
+      } else if (type.isEnum) {
+        var dartEnum = type.getDartType(fileGen);
+        named['valueOf'] = '$dartEnum.valueOf';
+        named['enumValues'] = '$dartEnum.values';
+      }
     }
-
+    assert(invocation != null);
     out.printAnnotated(
         'static final $_protobufImportPrefix.Extension $name = '
-        '$_protobufImportPrefix.Extension<$dartType>(\'$_extendedFullName\', \'$name\', '
-        '${_field.number}, ${_field.typeConstant}',
+        '$invocation(${ProtobufField._formatArguments(positionals, named)});\n',
         [
           NamedLocation(
               name: name,
               fieldPathSegment: List.from(fieldPath),
               start: 'static final $_protobufImportPrefix.Extension '.length)
         ]);
-
-    String initializer = _field.generateDefaultFunction(fileGen);
-
-    if (type.isMessage || type.isGroup) {
-      out.println(', $initializer, $dartType.create);');
-    } else if (type.isEnum) {
-      var dartEnum = type.getDartType(fileGen);
-      String enumParams = '(var v) => $dartEnum.valueOf(v), $dartEnum.values';
-      out.println(", $initializer, null, $enumParams);");
-    } else if (initializer != null) {
-      out.println(", $initializer);");
-    } else {
-      out.println(");");
-    }
   }
 }
diff --git a/protoc_plugin/lib/protobuf_field.dart b/protoc_plugin/lib/protobuf_field.dart
index 5032f25..6b8597d 100644
--- a/protoc_plugin/lib/protobuf_field.dart
+++ b/protoc_plugin/lib/protobuf_field.dart
@@ -45,6 +45,11 @@
   /// `null` for an extension.
   int get index => memberNames?.index;
 
+  String get quotedProtoName =>
+      (_unCamelCase(descriptor.jsonName) == descriptor.name)
+          ? null
+          : "'${descriptor.name}'";
+
   /// The position of this field as it appeared in the original DescriptorProto.
   int get sourcePosition => memberNames.sourcePosition;
 
@@ -119,90 +124,135 @@
         baseType.typeConstantSuffix;
   }
 
+  static String _formatArguments(
+      List<String> positionals, Map<String, String> named) {
+    final args = positionals.toList();
+    while (args.last == null) {
+      args.removeLast();
+    }
+    for (int i = 0; i < args.length; i++) {
+      if (args[i] == null) {
+        args[i] = 'null';
+      }
+    }
+    named.forEach((key, value) {
+      if (value != null) {
+        args.add('$key: $value');
+      }
+    });
+    return '${args.join(', ')}';
+  }
+
   /// Returns Dart code adding this field to a BuilderInfo object.
   /// The call will start with ".." and a method name.
   /// [fileGen] represents the .proto file where the code will be evaluated.
   String generateBuilderInfoCall(FileGenerator fileGen, String package) {
     assert(descriptor.hasJsonName());
     String quotedName = "'${descriptor.jsonName}'";
+
     String type = baseType.getDartType(fileGen);
 
+    String invocation;
+
+    List<String> args = <String>[];
+    Map<String, String> named = {'protoName': quotedProtoName};
+    args.add('$number');
+    args.add(quotedName);
+
     if (isMapField) {
       MessageGenerator generator = baseType.generator;
       ProtobufField key = generator._fieldList[0];
       ProtobufField value = generator._fieldList[1];
       String keyType = key.baseType.getDartType(fileGen);
       String valueType = value.baseType.getDartType(fileGen);
-      String keyTypeConstant = key.typeConstant;
-      String valTypeConstant = value.typeConstant;
-      String valueCreator = (value.baseType.isMessage || value.baseType.isGroup)
-          ? '$valueType.create'
-          : 'null';
-      String valueOf = value.baseType.isEnum ? '$valueType.valueOf' : 'null';
-      String enumValues = value.baseType.isEnum ? '$valueType.values' : 'null';
-      String mapEntryClassName = "'${generator.messageName}'";
-      String packageClause = package == ''
-          ? ''
-          : ", const $_protobufImportPrefix.PackageName(\'$package\')";
 
-      return '..m<$keyType, $valueType>($number, $quotedName, $mapEntryClassName,'
-          '$keyTypeConstant, $valTypeConstant, $valueCreator, $valueOf, $enumValues $packageClause)';
-    }
+      invocation = 'm<$keyType, $valueType>';
 
-    if (isRepeated) {
-      if (baseType.isMessage || baseType.isGroup) {
-        return '..pc<$type>($number, $quotedName, $typeConstant,'
-            '$type.create)';
+      named['entryClassName'] = "'${generator.messageName}'";
+      named['keyFieldType'] = key.typeConstant;
+      named['valueFieldType'] = value.typeConstant;
+      if (value.baseType.isMessage || value.baseType.isGroup) {
+        named['valueCreator'] = '$valueType.create';
       }
-      if (baseType.isEnum) {
-        return '..pc<$type>($number, $quotedName, $typeConstant,'
-            ' null, $type.valueOf, $type.values)';
+      if (value.baseType.isEnum) {
+        named['valueOf'] = '$valueType.valueOf';
+        named['enumValues'] = '$valueType.values';
       }
+      if (package != '') {
+        named['packageName'] =
+            'const $_protobufImportPrefix.PackageName(\'$package\')';
+      }
+    } else if (isRepeated) {
       if (typeConstant == '$_protobufImportPrefix.PbFieldType.PS') {
-        return '..pPS($number, $quotedName)';
+        invocation = 'pPS';
+      } else {
+        args.add(typeConstant);
+        if (baseType.isMessage || baseType.isGroup || baseType.isEnum) {
+          invocation = 'pc<$type>';
+        } else {
+          invocation = 'p<$type>';
+        }
+
+        if (baseType.isMessage || baseType.isGroup) {
+          named['subBuilder'] = '$type.create';
+        } else if (baseType.isEnum) {
+          named['valueOf'] = '$type.valueOf';
+          named['enumValues'] = '$type.values';
+        }
       }
-      return '..p<$type>($number, $quotedName, $typeConstant)';
-    }
+    } else {
+      // Singular field.
+      String makeDefault = generateDefaultFunction(fileGen);
 
-    String makeDefault = generateDefaultFunction(fileGen);
-    if (baseType.isEnum) {
-      String enumParams = '$type.valueOf, $type.values';
-      return '..e<$type>('
-          '$number, $quotedName, $typeConstant, $makeDefault, $enumParams)';
-    }
-
-    String prefix = '..a<$type>($number, $quotedName, $typeConstant';
-    if (makeDefault == null) {
-      switch (type) {
-        case '$_coreImportPrefix.String':
-          if (typeConstant == '$_protobufImportPrefix.PbFieldType.OS') {
-            return '..aOS($number, $quotedName)';
-          } else if (typeConstant == '$_protobufImportPrefix.PbFieldType.QS') {
-            return '..aQS($number, $quotedName)';
+      if (baseType.isEnum) {
+        args.add(typeConstant);
+        named['defaultOrMaker'] = makeDefault;
+        named['valueOf'] = '$type.valueOf';
+        named['enumValues'] = '$type.values';
+        invocation = 'e<$type>';
+      } else if (makeDefault == null) {
+        switch (type) {
+          case '$_coreImportPrefix.String':
+            if (typeConstant == '$_protobufImportPrefix.PbFieldType.OS') {
+              invocation = 'aOS';
+            } else if (typeConstant ==
+                '$_protobufImportPrefix.PbFieldType.QS') {
+              invocation = 'aQS';
+            } else {
+              invocation = 'a<$type>';
+              args.add(typeConstant);
+            }
+            break;
+          case '$_coreImportPrefix.bool':
+            if (typeConstant == '$_protobufImportPrefix.PbFieldType.OB') {
+              invocation = 'aOB';
+            } else {
+              invocation = 'a<$type>';
+              args.add(typeConstant);
+            }
+            break;
+          default:
+            invocation = 'a<$type>';
+            args.add(typeConstant);
+            break;
+        }
+      } else {
+        if (makeDefault == 'Int64.ZERO' &&
+            type == 'Int64' &&
+            typeConstant == '$_protobufImportPrefix.PbFieldType.O6') {
+          invocation = 'aInt64';
+        } else {
+          invocation = 'a<$type>';
+          named['defaultOrMaker'] = makeDefault;
+          args.add(typeConstant);
+          if (baseType.isMessage || baseType.isGroup) {
+            named['subBuilder'] = '$type.create';
           }
-          break;
-        case '$_coreImportPrefix.bool':
-          if (typeConstant == '$_protobufImportPrefix.PbFieldType.OB') {
-            return '..aOB($number, $quotedName)';
-          }
-          break;
-        default:
-          break;
+        }
       }
-      return prefix + ')';
     }
-
-    if (makeDefault == 'Int64.ZERO' &&
-        type == 'Int64' &&
-        typeConstant == '$_protobufImportPrefix.PbFieldType.O6') {
-      return '..aInt64($number, $quotedName)';
-    }
-
-    if (baseType.isMessage || baseType.isGroup) {
-      return prefix + ', $makeDefault, $type.create)';
-    }
-
-    return prefix + ', $makeDefault)';
+    assert(invocation != null);
+    return '..$invocation(${_formatArguments(args, named)})';
   }
 
   /// Returns a Dart expression that evaluates to this field's default value.
@@ -232,12 +282,7 @@
   /// [fileGen] represents the .proto file where the expression will be
   /// evaluated.
   String generateDefaultFunction(FileGenerator fileGen) {
-    if (isRepeated) {
-      return '() => $_protobufImportPrefix.PbList()';
-    }
-    bool sameProtoFile =
-        fileGen.protoFileUri == baseType.generator?.fileGen?.protoFileUri;
-
+    assert(!isRepeated);
     switch (descriptor.type) {
       case FieldDescriptorProto_Type.TYPE_BOOL:
         return _getDefaultAsBoolExpr(null);
@@ -290,10 +335,9 @@
         return '() => <$_coreImportPrefix.int>[$byteList]';
       case FieldDescriptorProto_Type.TYPE_GROUP:
       case FieldDescriptorProto_Type.TYPE_MESSAGE:
-        if (sameProtoFile) return '${baseType.unprefixed}.getDefault';
-        return "${baseType.prefixed}.getDefault";
+        return '${baseType.getDartType(fileGen)}.getDefault';
       case FieldDescriptorProto_Type.TYPE_ENUM:
-        var className = sameProtoFile ? baseType.unprefixed : baseType.prefixed;
+        var className = baseType.getDartType(fileGen);
         EnumGenerator gen = baseType.generator;
         if (descriptor.hasDefaultValue() &&
             descriptor.defaultValue.isNotEmpty) {
@@ -340,4 +384,11 @@
   _typeNotImplemented(String methodName) => "dart-protoc-plugin:"
       " $methodName not implemented for type (${descriptor.type})"
       " found in field $fullName";
+
+  static final RegExp _upperCase = RegExp('[A-Z]');
+
+  static String _unCamelCase(String name) {
+    return name.replaceAllMapped(
+        _upperCase, (match) => '_${match.group(0).toLowerCase()}');
+  }
 }
diff --git a/protoc_plugin/lib/src/dart_options.pb.dart b/protoc_plugin/lib/src/dart_options.pb.dart
index 4a02a78..74f381a 100644
--- a/protoc_plugin/lib/src/dart_options.pb.dart
+++ b/protoc_plugin/lib/src/dart_options.pb.dart
@@ -65,7 +65,8 @@
 class Imports extends $pb.GeneratedMessage {
   static final $pb.BuilderInfo _i =
       $pb.BuilderInfo('Imports', package: const $pb.PackageName('dart_options'))
-        ..pc<DartMixin>(1, 'mixins', $pb.PbFieldType.PM, DartMixin.create)
+        ..pc<DartMixin>(1, 'mixins', $pb.PbFieldType.PM,
+            subBuilder: DartMixin.create)
         ..hasRequiredFields = false;
 
   Imports._() : super();
@@ -92,12 +93,8 @@
 
 class Dart_options {
   static final $pb.Extension imports = $pb.Extension<Imports>(
-      'google.protobuf.FileOptions',
-      'imports',
-      28125061,
-      $pb.PbFieldType.OM,
-      Imports.getDefault,
-      Imports.create);
+      'google.protobuf.FileOptions', 'imports', 28125061, $pb.PbFieldType.OM,
+      defaultOrMaker: Imports.getDefault, subBuilder: Imports.create);
   static final $pb.Extension defaultMixin = $pb.Extension<$core.String>(
       'google.protobuf.FileOptions',
       'defaultMixin',
diff --git a/protoc_plugin/lib/src/descriptor.pb.dart b/protoc_plugin/lib/src/descriptor.pb.dart
index 9956f2e..4697c1a 100644
--- a/protoc_plugin/lib/src/descriptor.pb.dart
+++ b/protoc_plugin/lib/src/descriptor.pb.dart
@@ -18,8 +18,8 @@
 class FileDescriptorSet extends $pb.GeneratedMessage {
   static final $pb.BuilderInfo _i = $pb.BuilderInfo('FileDescriptorSet',
       package: const $pb.PackageName('google.protobuf'))
-    ..pc<FileDescriptorProto>(
-        1, 'file', $pb.PbFieldType.PM, FileDescriptorProto.create);
+    ..pc<FileDescriptorProto>(1, 'file', $pb.PbFieldType.PM,
+        subBuilder: FileDescriptorProto.create);
 
   FileDescriptorSet._() : super();
   factory FileDescriptorSet() => create();
@@ -51,18 +51,19 @@
     ..aOS(1, 'name')
     ..aOS(2, 'package')
     ..pPS(3, 'dependency')
-    ..pc<DescriptorProto>(
-        4, 'messageType', $pb.PbFieldType.PM, DescriptorProto.create)
-    ..pc<EnumDescriptorProto>(
-        5, 'enumType', $pb.PbFieldType.PM, EnumDescriptorProto.create)
-    ..pc<ServiceDescriptorProto>(
-        6, 'service', $pb.PbFieldType.PM, ServiceDescriptorProto.create)
-    ..pc<FieldDescriptorProto>(
-        7, 'extension', $pb.PbFieldType.PM, FieldDescriptorProto.create)
-    ..a<FileOptions>(8, 'options', $pb.PbFieldType.OM, FileOptions.getDefault,
-        FileOptions.create)
+    ..pc<DescriptorProto>(4, 'messageType', $pb.PbFieldType.PM,
+        subBuilder: DescriptorProto.create)
+    ..pc<EnumDescriptorProto>(5, 'enumType', $pb.PbFieldType.PM,
+        subBuilder: EnumDescriptorProto.create)
+    ..pc<ServiceDescriptorProto>(6, 'service', $pb.PbFieldType.PM,
+        subBuilder: ServiceDescriptorProto.create)
+    ..pc<FieldDescriptorProto>(7, 'extension', $pb.PbFieldType.PM,
+        subBuilder: FieldDescriptorProto.create)
+    ..a<FileOptions>(8, 'options', $pb.PbFieldType.OM,
+        defaultOrMaker: FileOptions.getDefault, subBuilder: FileOptions.create)
     ..a<SourceCodeInfo>(9, 'sourceCodeInfo', $pb.PbFieldType.OM,
-        SourceCodeInfo.getDefault, SourceCodeInfo.create)
+        defaultOrMaker: SourceCodeInfo.getDefault,
+        subBuilder: SourceCodeInfo.create)
     ..p<$core.int>(10, 'publicDependency', $pb.PbFieldType.P3)
     ..p<$core.int>(11, 'weakDependency', $pb.PbFieldType.P3)
     ..aOS(12, 'syntax');
@@ -247,22 +248,24 @@
   static final $pb.BuilderInfo _i = $pb.BuilderInfo('DescriptorProto',
       package: const $pb.PackageName('google.protobuf'))
     ..aOS(1, 'name')
-    ..pc<FieldDescriptorProto>(
-        2, 'field', $pb.PbFieldType.PM, FieldDescriptorProto.create)
-    ..pc<DescriptorProto>(
-        3, 'nestedType', $pb.PbFieldType.PM, DescriptorProto.create)
-    ..pc<EnumDescriptorProto>(
-        4, 'enumType', $pb.PbFieldType.PM, EnumDescriptorProto.create)
-    ..pc<DescriptorProto_ExtensionRange>(5, 'extensionRange',
-        $pb.PbFieldType.PM, DescriptorProto_ExtensionRange.create)
-    ..pc<FieldDescriptorProto>(
-        6, 'extension', $pb.PbFieldType.PM, FieldDescriptorProto.create)
+    ..pc<FieldDescriptorProto>(2, 'field', $pb.PbFieldType.PM,
+        subBuilder: FieldDescriptorProto.create)
+    ..pc<DescriptorProto>(3, 'nestedType', $pb.PbFieldType.PM,
+        subBuilder: DescriptorProto.create)
+    ..pc<EnumDescriptorProto>(4, 'enumType', $pb.PbFieldType.PM,
+        subBuilder: EnumDescriptorProto.create)
+    ..pc<DescriptorProto_ExtensionRange>(
+        5, 'extensionRange', $pb.PbFieldType.PM,
+        subBuilder: DescriptorProto_ExtensionRange.create)
+    ..pc<FieldDescriptorProto>(6, 'extension', $pb.PbFieldType.PM,
+        subBuilder: FieldDescriptorProto.create)
     ..a<MessageOptions>(7, 'options', $pb.PbFieldType.OM,
-        MessageOptions.getDefault, MessageOptions.create)
-    ..pc<OneofDescriptorProto>(
-        8, 'oneofDecl', $pb.PbFieldType.PM, OneofDescriptorProto.create)
+        defaultOrMaker: MessageOptions.getDefault,
+        subBuilder: MessageOptions.create)
+    ..pc<OneofDescriptorProto>(8, 'oneofDecl', $pb.PbFieldType.PM,
+        subBuilder: OneofDescriptorProto.create)
     ..pc<DescriptorProto_ReservedRange>(9, 'reservedRange', $pb.PbFieldType.PM,
-        DescriptorProto_ReservedRange.create)
+        subBuilder: DescriptorProto_ReservedRange.create)
     ..pPS(10, 'reservedName');
 
   DescriptorProto._() : super();
@@ -325,24 +328,19 @@
     ..aOS(1, 'name')
     ..aOS(2, 'extendee')
     ..a<$core.int>(3, 'number', $pb.PbFieldType.O3)
-    ..e<FieldDescriptorProto_Label>(
-        4,
-        'label',
-        $pb.PbFieldType.OE,
-        FieldDescriptorProto_Label.LABEL_OPTIONAL,
-        FieldDescriptorProto_Label.valueOf,
-        FieldDescriptorProto_Label.values)
-    ..e<FieldDescriptorProto_Type>(
-        5,
-        'type',
-        $pb.PbFieldType.OE,
-        FieldDescriptorProto_Type.TYPE_DOUBLE,
-        FieldDescriptorProto_Type.valueOf,
-        FieldDescriptorProto_Type.values)
+    ..e<FieldDescriptorProto_Label>(4, 'label', $pb.PbFieldType.OE,
+        defaultOrMaker: FieldDescriptorProto_Label.LABEL_OPTIONAL,
+        valueOf: FieldDescriptorProto_Label.valueOf,
+        enumValues: FieldDescriptorProto_Label.values)
+    ..e<FieldDescriptorProto_Type>(5, 'type', $pb.PbFieldType.OE,
+        defaultOrMaker: FieldDescriptorProto_Type.TYPE_DOUBLE,
+        valueOf: FieldDescriptorProto_Type.valueOf,
+        enumValues: FieldDescriptorProto_Type.values)
     ..aOS(6, 'typeName')
     ..aOS(7, 'defaultValue')
-    ..a<FieldOptions>(8, 'options', $pb.PbFieldType.OM, FieldOptions.getDefault,
-        FieldOptions.create)
+    ..a<FieldOptions>(8, 'options', $pb.PbFieldType.OM,
+        defaultOrMaker: FieldOptions.getDefault,
+        subBuilder: FieldOptions.create)
     ..a<$core.int>(9, 'oneofIndex', $pb.PbFieldType.O3)
     ..aOS(10, 'jsonName');
 
@@ -453,8 +451,9 @@
   static final $pb.BuilderInfo _i = $pb.BuilderInfo('OneofDescriptorProto',
       package: const $pb.PackageName('google.protobuf'))
     ..aOS(1, 'name')
-    ..a<OneofOptions>(2, 'options', $pb.PbFieldType.OM, OneofOptions.getDefault,
-        OneofOptions.create);
+    ..a<OneofOptions>(2, 'options', $pb.PbFieldType.OM,
+        defaultOrMaker: OneofOptions.getDefault,
+        subBuilder: OneofOptions.create);
 
   OneofDescriptorProto._() : super();
   factory OneofDescriptorProto() => create();
@@ -499,10 +498,10 @@
   static final $pb.BuilderInfo _i = $pb.BuilderInfo('EnumDescriptorProto',
       package: const $pb.PackageName('google.protobuf'))
     ..aOS(1, 'name')
-    ..pc<EnumValueDescriptorProto>(
-        2, 'value', $pb.PbFieldType.PM, EnumValueDescriptorProto.create)
-    ..a<EnumOptions>(3, 'options', $pb.PbFieldType.OM, EnumOptions.getDefault,
-        EnumOptions.create);
+    ..pc<EnumValueDescriptorProto>(2, 'value', $pb.PbFieldType.PM,
+        subBuilder: EnumValueDescriptorProto.create)
+    ..a<EnumOptions>(3, 'options', $pb.PbFieldType.OM,
+        defaultOrMaker: EnumOptions.getDefault, subBuilder: EnumOptions.create);
 
   EnumDescriptorProto._() : super();
   factory EnumDescriptorProto() => create();
@@ -550,7 +549,8 @@
     ..aOS(1, 'name')
     ..a<$core.int>(2, 'number', $pb.PbFieldType.O3)
     ..a<EnumValueOptions>(3, 'options', $pb.PbFieldType.OM,
-        EnumValueOptions.getDefault, EnumValueOptions.create);
+        defaultOrMaker: EnumValueOptions.getDefault,
+        subBuilder: EnumValueOptions.create);
 
   EnumValueDescriptorProto._() : super();
   factory EnumValueDescriptorProto() => create();
@@ -604,10 +604,11 @@
   static final $pb.BuilderInfo _i = $pb.BuilderInfo('ServiceDescriptorProto',
       package: const $pb.PackageName('google.protobuf'))
     ..aOS(1, 'name')
-    ..pc<MethodDescriptorProto>(
-        2, 'method', $pb.PbFieldType.PM, MethodDescriptorProto.create)
+    ..pc<MethodDescriptorProto>(2, 'method', $pb.PbFieldType.PM,
+        subBuilder: MethodDescriptorProto.create)
     ..a<ServiceOptions>(3, 'options', $pb.PbFieldType.OM,
-        ServiceOptions.getDefault, ServiceOptions.create);
+        defaultOrMaker: ServiceOptions.getDefault,
+        subBuilder: ServiceOptions.create);
 
   ServiceDescriptorProto._() : super();
   factory ServiceDescriptorProto() => create();
@@ -658,7 +659,8 @@
     ..aOS(2, 'inputType')
     ..aOS(3, 'outputType')
     ..a<MethodOptions>(4, 'options', $pb.PbFieldType.OM,
-        MethodOptions.getDefault, MethodOptions.create)
+        defaultOrMaker: MethodOptions.getDefault,
+        subBuilder: MethodOptions.create)
     ..aOB(5, 'clientStreaming')
     ..aOB(6, 'serverStreaming');
 
@@ -739,13 +741,10 @@
       package: const $pb.PackageName('google.protobuf'))
     ..aOS(1, 'javaPackage')
     ..aOS(8, 'javaOuterClassname')
-    ..e<FileOptions_OptimizeMode>(
-        9,
-        'optimizeFor',
-        $pb.PbFieldType.OE,
-        FileOptions_OptimizeMode.SPEED,
-        FileOptions_OptimizeMode.valueOf,
-        FileOptions_OptimizeMode.values)
+    ..e<FileOptions_OptimizeMode>(9, 'optimizeFor', $pb.PbFieldType.OE,
+        defaultOrMaker: FileOptions_OptimizeMode.SPEED,
+        valueOf: FileOptions_OptimizeMode.valueOf,
+        enumValues: FileOptions_OptimizeMode.values)
     ..aOB(10, 'javaMultipleFiles')
     ..aOS(11, 'goPackage')
     ..aOB(16, 'ccGenericServices')
@@ -761,7 +760,7 @@
     ..aOS(40, 'phpClassPrefix')
     ..aOS(41, 'phpNamespace')
     ..pc<UninterpretedOption>(999, 'uninterpretedOption', $pb.PbFieldType.PM,
-        UninterpretedOption.create)
+        subBuilder: UninterpretedOption.create)
     ..hasExtensions = true;
 
   FileOptions._() : super();
@@ -934,7 +933,7 @@
     ..aOB(3, 'deprecated')
     ..aOB(7, 'mapEntry')
     ..pc<UninterpretedOption>(999, 'uninterpretedOption', $pb.PbFieldType.PM,
-        UninterpretedOption.create)
+        subBuilder: UninterpretedOption.create)
     ..hasExtensions = true;
 
   MessageOptions._() : super();
@@ -995,26 +994,20 @@
 class FieldOptions extends $pb.GeneratedMessage {
   static final $pb.BuilderInfo _i = $pb.BuilderInfo('FieldOptions',
       package: const $pb.PackageName('google.protobuf'))
-    ..e<FieldOptions_CType>(
-        1,
-        'ctype',
-        $pb.PbFieldType.OE,
-        FieldOptions_CType.STRING,
-        FieldOptions_CType.valueOf,
-        FieldOptions_CType.values)
+    ..e<FieldOptions_CType>(1, 'ctype', $pb.PbFieldType.OE,
+        defaultOrMaker: FieldOptions_CType.STRING,
+        valueOf: FieldOptions_CType.valueOf,
+        enumValues: FieldOptions_CType.values)
     ..aOB(2, 'packed')
     ..aOB(3, 'deprecated')
     ..aOB(5, 'lazy')
-    ..e<FieldOptions_JSType>(
-        6,
-        'jstype',
-        $pb.PbFieldType.OE,
-        FieldOptions_JSType.JS_NORMAL,
-        FieldOptions_JSType.valueOf,
-        FieldOptions_JSType.values)
+    ..e<FieldOptions_JSType>(6, 'jstype', $pb.PbFieldType.OE,
+        defaultOrMaker: FieldOptions_JSType.JS_NORMAL,
+        valueOf: FieldOptions_JSType.valueOf,
+        enumValues: FieldOptions_JSType.values)
     ..aOB(10, 'weak')
     ..pc<UninterpretedOption>(999, 'uninterpretedOption', $pb.PbFieldType.PM,
-        UninterpretedOption.create)
+        subBuilder: UninterpretedOption.create)
     ..hasExtensions = true;
 
   FieldOptions._() : super();
@@ -1092,7 +1085,7 @@
   static final $pb.BuilderInfo _i = $pb.BuilderInfo('OneofOptions',
       package: const $pb.PackageName('google.protobuf'))
     ..pc<UninterpretedOption>(999, 'uninterpretedOption', $pb.PbFieldType.PM,
-        UninterpretedOption.create)
+        subBuilder: UninterpretedOption.create)
     ..hasExtensions = true;
 
   OneofOptions._() : super();
@@ -1124,7 +1117,7 @@
     ..aOB(2, 'allowAlias')
     ..aOB(3, 'deprecated')
     ..pc<UninterpretedOption>(999, 'uninterpretedOption', $pb.PbFieldType.PM,
-        UninterpretedOption.create)
+        subBuilder: UninterpretedOption.create)
     ..hasExtensions = true;
 
   EnumOptions._() : super();
@@ -1170,7 +1163,7 @@
       package: const $pb.PackageName('google.protobuf'))
     ..aOB(1, 'deprecated')
     ..pc<UninterpretedOption>(999, 'uninterpretedOption', $pb.PbFieldType.PM,
-        UninterpretedOption.create)
+        subBuilder: UninterpretedOption.create)
     ..hasExtensions = true;
 
   EnumValueOptions._() : super();
@@ -1210,7 +1203,7 @@
       package: const $pb.PackageName('google.protobuf'))
     ..aOB(33, 'deprecated')
     ..pc<UninterpretedOption>(999, 'uninterpretedOption', $pb.PbFieldType.PM,
-        UninterpretedOption.create)
+        subBuilder: UninterpretedOption.create)
     ..hasExtensions = true;
 
   ServiceOptions._() : super();
@@ -1249,14 +1242,12 @@
       package: const $pb.PackageName('google.protobuf'))
     ..aOB(33, 'deprecated')
     ..e<MethodOptions_IdempotencyLevel>(
-        34,
-        'idempotencyLevel',
-        $pb.PbFieldType.OE,
-        MethodOptions_IdempotencyLevel.IDEMPOTENCY_UNKNOWN,
-        MethodOptions_IdempotencyLevel.valueOf,
-        MethodOptions_IdempotencyLevel.values)
+        34, 'idempotencyLevel', $pb.PbFieldType.OE,
+        defaultOrMaker: MethodOptions_IdempotencyLevel.IDEMPOTENCY_UNKNOWN,
+        valueOf: MethodOptions_IdempotencyLevel.valueOf,
+        enumValues: MethodOptions_IdempotencyLevel.values)
     ..pc<UninterpretedOption>(999, 'uninterpretedOption', $pb.PbFieldType.PM,
-        UninterpretedOption.create)
+        subBuilder: UninterpretedOption.create)
     ..hasExtensions = true;
 
   MethodOptions._() : super();
@@ -1350,10 +1341,11 @@
 class UninterpretedOption extends $pb.GeneratedMessage {
   static final $pb.BuilderInfo _i = $pb.BuilderInfo('UninterpretedOption',
       package: const $pb.PackageName('google.protobuf'))
-    ..pc<UninterpretedOption_NamePart>(
-        2, 'name', $pb.PbFieldType.PM, UninterpretedOption_NamePart.create)
+    ..pc<UninterpretedOption_NamePart>(2, 'name', $pb.PbFieldType.PM,
+        subBuilder: UninterpretedOption_NamePart.create)
     ..aOS(3, 'identifierValue')
-    ..a<Int64>(4, 'positiveIntValue', $pb.PbFieldType.OU6, Int64.ZERO)
+    ..a<Int64>(4, 'positiveIntValue', $pb.PbFieldType.OU6,
+        defaultOrMaker: Int64.ZERO)
     ..aInt64(5, 'negativeIntValue')
     ..a<$core.double>(6, 'doubleValue', $pb.PbFieldType.OD)
     ..a<$core.List<$core.int>>(7, 'stringValue', $pb.PbFieldType.OY)
@@ -1490,8 +1482,8 @@
 class SourceCodeInfo extends $pb.GeneratedMessage {
   static final $pb.BuilderInfo _i = $pb.BuilderInfo('SourceCodeInfo',
       package: const $pb.PackageName('google.protobuf'))
-    ..pc<SourceCodeInfo_Location>(
-        1, 'location', $pb.PbFieldType.PM, SourceCodeInfo_Location.create)
+    ..pc<SourceCodeInfo_Location>(1, 'location', $pb.PbFieldType.PM,
+        subBuilder: SourceCodeInfo_Location.create)
     ..hasRequiredFields = false;
 
   SourceCodeInfo._() : super();
@@ -1583,7 +1575,7 @@
   static final $pb.BuilderInfo _i = $pb.BuilderInfo('GeneratedCodeInfo',
       package: const $pb.PackageName('google.protobuf'))
     ..pc<GeneratedCodeInfo_Annotation>(1, 'annotation', $pb.PbFieldType.PM,
-        GeneratedCodeInfo_Annotation.create)
+        subBuilder: GeneratedCodeInfo_Annotation.create)
     ..hasRequiredFields = false;
 
   GeneratedCodeInfo._() : super();
diff --git a/protoc_plugin/lib/src/plugin.pb.dart b/protoc_plugin/lib/src/plugin.pb.dart
index 7735711..e7e57f3 100644
--- a/protoc_plugin/lib/src/plugin.pb.dart
+++ b/protoc_plugin/lib/src/plugin.pb.dart
@@ -78,10 +78,10 @@
       package: const $pb.PackageName('google.protobuf.compiler'))
     ..pPS(1, 'fileToGenerate')
     ..aOS(2, 'parameter')
-    ..a<Version>(3, 'compilerVersion', $pb.PbFieldType.OM, Version.getDefault,
-        Version.create)
-    ..pc<$0.FileDescriptorProto>(
-        15, 'protoFile', $pb.PbFieldType.PM, $0.FileDescriptorProto.create);
+    ..a<Version>(3, 'compilerVersion', $pb.PbFieldType.OM,
+        defaultOrMaker: Version.getDefault, subBuilder: Version.create)
+    ..pc<$0.FileDescriptorProto>(15, 'protoFile', $pb.PbFieldType.PM,
+        subBuilder: $0.FileDescriptorProto.create);
 
   CodeGeneratorRequest._() : super();
   factory CodeGeneratorRequest() => create();
@@ -188,8 +188,8 @@
   static final $pb.BuilderInfo _i = $pb.BuilderInfo('CodeGeneratorResponse',
       package: const $pb.PackageName('google.protobuf.compiler'))
     ..aOS(1, 'error')
-    ..pc<CodeGeneratorResponse_File>(
-        15, 'file', $pb.PbFieldType.PM, CodeGeneratorResponse_File.create)
+    ..pc<CodeGeneratorResponse_File>(15, 'file', $pb.PbFieldType.PM,
+        subBuilder: CodeGeneratorResponse_File.create)
     ..hasRequiredFields = false;
 
   CodeGeneratorResponse._() : super();
diff --git a/protoc_plugin/pubspec.yaml b/protoc_plugin/pubspec.yaml
index 869d142..929516f 100644
--- a/protoc_plugin/pubspec.yaml
+++ b/protoc_plugin/pubspec.yaml
@@ -1,5 +1,5 @@
 name: protoc_plugin
-version: 18.0.0-dev
+version: 18.0.0
 author: Dart Team <misc@dartlang.org>
 description: Protoc compiler plugin to generate Dart code
 homepage: https://github.com/dart-lang/protobuf
@@ -10,7 +10,7 @@
 dependencies:
   fixnum: ^0.10.9
   path: ^1.0.0
-  protobuf: ^0.13.10
+  protobuf: ^0.14.0
   dart_style: ^1.0.6
 
 dev_dependencies:
diff --git a/protoc_plugin/test/goldens/imports.pb b/protoc_plugin/test/goldens/imports.pb
index 59fdce5..69888cf 100644
--- a/protoc_plugin/test/goldens/imports.pb
+++ b/protoc_plugin/test/goldens/imports.pb
@@ -14,9 +14,9 @@
 
 class M extends $pb.GeneratedMessage {
   static final $pb.BuilderInfo _i = $pb.BuilderInfo('M')
-    ..a<M>(1, 'm', $pb.PbFieldType.OM, M.getDefault, M.create)
-    ..a<$1.M>(2, 'm1', $pb.PbFieldType.OM, $1.M.getDefault, $1.M.create)
-    ..a<$2.M>(3, 'm2', $pb.PbFieldType.OM, $2.M.getDefault, $2.M.create)
+    ..a<M>(1, 'm', $pb.PbFieldType.OM, defaultOrMaker: M.getDefault, subBuilder: M.create)
+    ..a<$1.M>(2, 'm1', $pb.PbFieldType.OM, defaultOrMaker: $1.M.getDefault, subBuilder: $1.M.create)
+    ..a<$2.M>(3, 'm2', $pb.PbFieldType.OM, defaultOrMaker: $2.M.getDefault, subBuilder: $2.M.create)
     ..hasRequiredFields = false
   ;
 
diff --git a/protoc_plugin/test/goldens/messageGenerator b/protoc_plugin/test/goldens/messageGenerator
index 8c1516a..7a80061 100644
--- a/protoc_plugin/test/goldens/messageGenerator
+++ b/protoc_plugin/test/goldens/messageGenerator
@@ -1,8 +1,8 @@
 class PhoneNumber extends $pb.GeneratedMessage {
   static final $pb.BuilderInfo _i = $pb.BuilderInfo('PhoneNumber')
     ..aQS(1, 'number')
-    ..e<PhoneNumber_PhoneType>(2, 'type', $pb.PbFieldType.OE, PhoneNumber_PhoneType.MOBILE, PhoneNumber_PhoneType.valueOf, PhoneNumber_PhoneType.values)
-    ..a<$core.String>(3, 'name', $pb.PbFieldType.OS, '\$')
+    ..e<PhoneNumber_PhoneType>(2, 'type', $pb.PbFieldType.OE, defaultOrMaker: PhoneNumber_PhoneType.MOBILE, valueOf: PhoneNumber_PhoneType.valueOf, enumValues: PhoneNumber_PhoneType.values)
+    ..a<$core.String>(3, 'name', $pb.PbFieldType.OS, defaultOrMaker: '\$')
     ..aOS(4, 'deprecatedField')
   ;
 
diff --git a/protoc_plugin/test/goldens/messageGenerator.meta b/protoc_plugin/test/goldens/messageGenerator.meta
index 17d9fed..3590367 100644
--- a/protoc_plugin/test/goldens/messageGenerator.meta
+++ b/protoc_plugin/test/goldens/messageGenerator.meta
@@ -9,8 +9,8 @@
   path: 4
   path: 0
   sourceFile: 
-  begin: 390
-  end: 401
+  begin: 443
+  end: 454
 }
 annotation: {
   path: 4
@@ -18,8 +18,8 @@
   path: 2
   path: 1
   sourceFile: 
-  begin: 1318
-  end: 1324
+  begin: 1371
+  end: 1377
 }
 annotation: {
   path: 4
@@ -27,8 +27,8 @@
   path: 2
   path: 1
   sourceFile: 
-  begin: 1349
-  end: 1355
+  begin: 1402
+  end: 1408
 }
 annotation: {
   path: 4
@@ -36,8 +36,8 @@
   path: 2
   path: 1
   sourceFile: 
-  begin: 1408
-  end: 1417
+  begin: 1461
+  end: 1470
 }
 annotation: {
   path: 4
@@ -45,8 +45,8 @@
   path: 2
   path: 1
   sourceFile: 
-  begin: 1440
-  end: 1451
+  begin: 1493
+  end: 1504
 }
 annotation: {
   path: 4
@@ -54,8 +54,8 @@
   path: 2
   path: 0
   sourceFile: 
-  begin: 1501
-  end: 1505
+  begin: 1554
+  end: 1558
 }
 annotation: {
   path: 4
@@ -63,8 +63,8 @@
   path: 2
   path: 0
   sourceFile: 
-  begin: 1526
-  end: 1530
+  begin: 1579
+  end: 1583
 }
 annotation: {
   path: 4
@@ -72,8 +72,8 @@
   path: 2
   path: 0
   sourceFile: 
-  begin: 1589
-  end: 1596
+  begin: 1642
+  end: 1649
 }
 annotation: {
   path: 4
@@ -81,8 +81,8 @@
   path: 2
   path: 0
   sourceFile: 
-  begin: 1619
-  end: 1628
+  begin: 1672
+  end: 1681
 }
 annotation: {
   path: 4
@@ -90,8 +90,8 @@
   path: 2
   path: 2
   sourceFile: 
-  begin: 1669
-  end: 1673
+  begin: 1722
+  end: 1726
 }
 annotation: {
   path: 4
@@ -99,8 +99,8 @@
   path: 2
   path: 2
   sourceFile: 
-  begin: 1700
-  end: 1704
+  begin: 1753
+  end: 1757
 }
 annotation: {
   path: 4
@@ -108,8 +108,8 @@
   path: 2
   path: 2
   sourceFile: 
-  begin: 1757
-  end: 1764
+  begin: 1810
+  end: 1817
 }
 annotation: {
   path: 4
@@ -117,8 +117,8 @@
   path: 2
   path: 2
   sourceFile: 
-  begin: 1787
-  end: 1796
+  begin: 1840
+  end: 1849
 }
 annotation: {
   path: 4
@@ -126,8 +126,8 @@
   path: 2
   path: 3
   sourceFile: 
-  begin: 1886
-  end: 1901
+  begin: 1939
+  end: 1954
 }
 annotation: {
   path: 4
@@ -135,8 +135,8 @@
   path: 2
   path: 3
   sourceFile: 
-  begin: 1975
-  end: 1990
+  begin: 2028
+  end: 2043
 }
 annotation: {
   path: 4
@@ -144,8 +144,8 @@
   path: 2
   path: 3
   sourceFile: 
-  begin: 2092
-  end: 2110
+  begin: 2145
+  end: 2163
 }
 annotation: {
   path: 4
@@ -153,6 +153,6 @@
   path: 2
   path: 3
   sourceFile: 
-  begin: 2182
-  end: 2202
+  begin: 2235
+  end: 2255
 }
diff --git a/protoc_plugin/test/goldens/oneMessage.pb b/protoc_plugin/test/goldens/oneMessage.pb
index 017d5b8..e728680 100644
--- a/protoc_plugin/test/goldens/oneMessage.pb
+++ b/protoc_plugin/test/goldens/oneMessage.pb
@@ -13,7 +13,7 @@
   static final $pb.BuilderInfo _i = $pb.BuilderInfo('PhoneNumber')
     ..aQS(1, 'number')
     ..a<$core.int>(2, 'type', $pb.PbFieldType.O3)
-    ..a<$core.String>(3, 'name', $pb.PbFieldType.OS, '\$')
+    ..a<$core.String>(3, 'name', $pb.PbFieldType.OS, defaultOrMaker: '\$')
   ;
 
   PhoneNumber._() : super();
diff --git a/protoc_plugin/test/goldens/oneMessage.pb.meta b/protoc_plugin/test/goldens/oneMessage.pb.meta
index 23f9193..f9986a6 100644
--- a/protoc_plugin/test/goldens/oneMessage.pb.meta
+++ b/protoc_plugin/test/goldens/oneMessage.pb.meta
@@ -9,8 +9,8 @@
   path: 4
   path: 0
   sourceFile: test
-  begin: 620
-  end: 631
+  begin: 636
+  end: 647
 }
 annotation: {
   path: 4
@@ -18,8 +18,8 @@
   path: 2
   path: 0
   sourceFile: test
-  begin: 1548
-  end: 1554
+  begin: 1564
+  end: 1570
 }
 annotation: {
   path: 4
@@ -27,8 +27,8 @@
   path: 2
   path: 0
   sourceFile: test
-  begin: 1579
-  end: 1585
+  begin: 1595
+  end: 1601
 }
 annotation: {
   path: 4
@@ -36,8 +36,8 @@
   path: 2
   path: 0
   sourceFile: test
-  begin: 1638
-  end: 1647
+  begin: 1654
+  end: 1663
 }
 annotation: {
   path: 4
@@ -45,8 +45,8 @@
   path: 2
   path: 0
   sourceFile: test
-  begin: 1670
-  end: 1681
+  begin: 1686
+  end: 1697
 }
 annotation: {
   path: 4
@@ -54,8 +54,8 @@
   path: 2
   path: 1
   sourceFile: test
-  begin: 1719
-  end: 1723
+  begin: 1735
+  end: 1739
 }
 annotation: {
   path: 4
@@ -63,8 +63,8 @@
   path: 2
   path: 1
   sourceFile: test
-  begin: 1746
-  end: 1750
+  begin: 1762
+  end: 1766
 }
 annotation: {
   path: 4
@@ -72,8 +72,8 @@
   path: 2
   path: 1
   sourceFile: test
-  begin: 1805
-  end: 1812
+  begin: 1821
+  end: 1828
 }
 annotation: {
   path: 4
@@ -81,8 +81,8 @@
   path: 2
   path: 1
   sourceFile: test
-  begin: 1835
-  end: 1844
+  begin: 1851
+  end: 1860
 }
 annotation: {
   path: 4
@@ -90,8 +90,8 @@
   path: 2
   path: 2
   sourceFile: test
-  begin: 1885
-  end: 1889
+  begin: 1901
+  end: 1905
 }
 annotation: {
   path: 4
@@ -99,8 +99,8 @@
   path: 2
   path: 2
   sourceFile: test
-  begin: 1916
-  end: 1920
+  begin: 1932
+  end: 1936
 }
 annotation: {
   path: 4
@@ -108,8 +108,8 @@
   path: 2
   path: 2
   sourceFile: test
-  begin: 1973
-  end: 1980
+  begin: 1989
+  end: 1996
 }
 annotation: {
   path: 4
@@ -117,6 +117,6 @@
   path: 2
   path: 2
   sourceFile: test
-  begin: 2003
-  end: 2012
+  begin: 2019
+  end: 2028
 }