Skip to content

Commit

Permalink
Move protobuf enums to a separate .pbenum.dart file.
Browse files Browse the repository at this point in the history
This is to reduce the amount of Dart code that Dartium needs to
load when only the enums from a .proto file are needed.

Reviewed internally: cl/123342330
  • Loading branch information
Brian Slesinsky committed May 26, 2016
1 parent 63292ef commit f032601
Show file tree
Hide file tree
Showing 9 changed files with 198 additions and 97 deletions.
13 changes: 7 additions & 6 deletions lib/bazel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -105,22 +105,23 @@ class BazelOutputConfiguration extends DefaultOutputConfiguration {
}

@override
Uri outputPathFor(Uri input) {
Uri outputPathFor(Uri input, String extension) {
var pkg = _findPackage(input.path);
if (pkg == null) {
throw new ArgumentError('Unable to locate package for input $input.');
}

// Bazel package-relative paths.
var relativeInput = input.path.substring('${pkg.input_root}/'.length);
var relativeOutput = replacePathExtension(relativeInput);
var outputPath = p.join(pkg.output_root, relativeOutput);
var base = p.withoutExtension(relativeInput);
var outputPath = p.join(pkg.output_root, "$base$extension");
return new Uri.file(outputPath);
}

@override
Uri resolveImport(Uri target, Uri source) {
var targetUri = _packageUriFor(replacePathExtension(target.path));
Uri resolveImport(Uri target, Uri source, String extension) {
var targetBase = p.withoutExtension(target.path);
var targetUri = _packageUriFor("$targetBase$extension");
var sourceUri = _packageUriFor(source.path);

if (targetUri == null && sourceUri != null) {
Expand All @@ -134,7 +135,7 @@ class BazelOutputConfiguration extends DefaultOutputConfiguration {
return targetUri.uri;
}

return super.resolveImport(target, source);
return super.resolveImport(target, source, extension);
}

_PackageUri _packageUriFor(String target) {
Expand Down
1 change: 1 addition & 0 deletions lib/code_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class CodeGenerator extends ProtobufContainer {
var name = gen._fileDescriptor.name;
if (request.fileToGenerate.contains(name)) {
response.file.add(gen.generateResponse(config));
response.file.add(gen.generateEnumResponse(config));
response.file.add(gen.generateJsonDartResponse(config));
}
}
Expand Down
7 changes: 6 additions & 1 deletion lib/extension_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,20 @@ class ExtensionGenerator {
///
/// For each .pb.dart file that the generated code needs to import,
/// add its generator.
void addImportsTo(Set<FileGenerator> imports) {
void addImportsTo(
Set<FileGenerator> imports, Set<FileGenerator> enumImports) {
if (_field == null) throw new StateError("resolve not called");
var typeGen = _field.baseType.generator;
if (typeGen != null && typeGen.fileGen != fileGen) {
// The type of this extension is defined in a different file,
// so we need to import it.
if (typeGen is EnumGenerator) {
enumImports.add(typeGen.fileGen);
} else {
imports.add(typeGen.fileGen);
}
}
}

/// Adds dependencies of [generateConstants] to [imports].
///
Expand Down
124 changes: 104 additions & 20 deletions lib/file_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ class FileGenerator extends ProtobufContainer {

Uri filePath = new Uri.file(_fileDescriptor.name);
return new CodeGeneratorResponse_File()
..name = config.outputPathFor(filePath).path
..name = config.outputPathFor(filePath, ".pb.dart").path
..content = out.toString();
}

Expand All @@ -130,9 +130,6 @@ class FileGenerator extends ProtobufContainer {
generateHeader(out, config);

// Generate code.
for (EnumGenerator e in enumGenerators) {
e.generate(out);
}
for (MessageGenerator m in messageGenerators) {
m.generate(out);
}
Expand Down Expand Up @@ -181,7 +178,11 @@ class FileGenerator extends ProtobufContainer {
if (_needsFixnumImport) {
out.println("import 'package:fixnum/fixnum.dart';");
}

if (_needsProtobufImport) {
out.println("import 'package:protobuf/protobuf.dart';");
out.println();
}

var mixinImports = findMixinsToImport();
var importNames = mixinImports.keys.toList();
Expand All @@ -190,23 +191,46 @@ class FileGenerator extends ProtobufContainer {
var symbols = mixinImports[imp];
out.println("import '${imp}' show ${symbols.join(', ')};");
}
if (mixinImports.isNotEmpty) out.println();

// Import the .pb.dart files we depend on.
for (var imported in _findProtosToImport()) {
var imports = new Set<FileGenerator>.identity();
var enumImports = new Set<FileGenerator>.identity();
_findProtosToImport(imports, enumImports);

void writeImport(FileGenerator target, String extension) {
Uri resolvedImport =
config.resolveImport(imported.protoFileUri, protoFileUri);
config.resolveImport(target.protoFileUri, protoFileUri, extension);
out.print("import '$resolvedImport'");
if (package != imported.package && imported.package.isNotEmpty) {
out.print(' as ${imported.packageImportPrefix}');
if (package != target.package && target.package.isNotEmpty) {
out.print(' as ${target.packageImportPrefix}');
}
out.println(';');
}
out.println();

for (var target in imports) {
writeImport(target, ".pb.dart");
}
if (imports.isNotEmpty) out.println();

for (var target in enumImports) {
writeImport(target, ".pbenum.dart");
}
if (enumImports.isNotEmpty) out.println();

// Services also depend on the json imports.
if (serviceGenerators.isNotEmpty) {
Uri resolvedImport = config.resolveJsonImport(protoFileUri, protoFileUri);
out.print("import '$resolvedImport';");
Uri resolvedImport =
config.resolveImport(protoFileUri, protoFileUri, ".pbjson.dart");
out.println("import '$resolvedImport';");
out.println();
}

// Export enums in main file for backward compatibility.
if (enumCount > 0) {
Uri resolvedImport =
config.resolveImport(protoFileUri, protoFileUri, ".pbenum.dart");
out.println("export '$resolvedImport';");
out.println();
}
}
Expand All @@ -221,20 +245,26 @@ class FileGenerator extends ProtobufContainer {
return false;
}

bool get _needsProtobufImport =>
messageGenerators.isNotEmpty ||
extensionGenerators.isNotEmpty ||
clientApiGenerators.isNotEmpty ||
serviceGenerators.isNotEmpty;

/// Returns the generator for each .pb.dart file we need to import.
Set<FileGenerator> _findProtosToImport() {
var imports = new Set<FileGenerator>.identity();
void _findProtosToImport(
Set<FileGenerator> imports, Set<FileGenerator> enumImports) {
for (var m in messageGenerators) {
m.addImportsTo(imports);
m.addImportsTo(imports, enumImports);
}
for (var x in extensionGenerators) {
x.addImportsTo(imports);
x.addImportsTo(imports, enumImports);
}
for (var x in serviceGenerators) {
x.addImportsTo(imports);
}
imports.remove(this); // Don't need to import self.
return imports;
// Don't need to import self. (But we may need to import the enums.)
imports.remove(this);
}

/// Returns a map from import names to the Dart symbols to be imported.
Expand Down Expand Up @@ -262,6 +292,60 @@ class FileGenerator extends ProtobufContainer {
return imports;
}

CodeGeneratorResponse_File generateEnumResponse(OutputConfiguration config) {
if (!_linked) throw new StateError("not linked");

IndentingWriter out = new IndentingWriter();

generateEnumFile(out, config);

Uri filePath = new Uri.file(_fileDescriptor.name);
return new CodeGeneratorResponse_File()
..name = config.outputPathFor(filePath, ".pbenum.dart").path
..content = out.toString();
}

void generateEnumFile(IndentingWriter out,
[OutputConfiguration config = const DefaultOutputConfiguration()]) {
Uri filePath = new Uri.file(_fileDescriptor.name);
if (filePath.isAbsolute) {
// protoc should never generate a file descriptor with an absolute path.
throw "FAILURE: File with an absolute path is not supported";
}

var baseLibraryName = _generateLibraryName(filePath);
var libraryName = baseLibraryName + "_pbenum";
out.print('''
///
// Generated code. Do not modify.
///
library $libraryName;
''');

if (enumCount > 0) {
out.println("import 'package:protobuf/protobuf.dart';");
out.println();
}

for (EnumGenerator e in enumGenerators) {
e.generate(out);
}

for (MessageGenerator m in messageGenerators) {
m.generateEnums(out);
}
}

/// Returns the number of enum types generated in the .pbenum.dart file.
int get enumCount {
var count = enumGenerators.length;
for (MessageGenerator m in messageGenerators) {
count += m.enumCount;
}
return count;
}

CodeGeneratorResponse_File generateJsonDartResponse(
OutputConfiguration config) {
if (!_linked) throw new StateError("not linked");
Expand All @@ -272,7 +356,7 @@ class FileGenerator extends ProtobufContainer {

Uri filePath = new Uri.file(_fileDescriptor.name);
return new CodeGeneratorResponse_File()
..name = config.jsonDartOutputPathFor(filePath).path
..name = config.outputPathFor(filePath, ".pbjson.dart").path
..content = out.toString();
}

Expand All @@ -297,8 +381,8 @@ library $libraryName;
// Import the .pbjson.dart files we depend on.
var importList = _findJsonProtosToImport();
for (var imported in importList) {
Uri resolvedImport =
config.resolveJsonImport(imported.protoFileUri, protoFileUri);
Uri resolvedImport = config.resolveImport(
imported.protoFileUri, protoFileUri, ".pbjson.dart");
out.print("import '$resolvedImport'");
if (package != imported.package && imported.package.isNotEmpty) {
out.print(' as ${imported.packageImportPrefix}');
Expand Down
36 changes: 26 additions & 10 deletions lib/message_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -197,24 +197,34 @@ class MessageGenerator extends ProtobufContainer {
///
/// For each .pb.dart file that the generated code needs to import,
/// add its generator.
void addImportsTo(Set<FileGenerator> imports) {
void addImportsTo(
Set<FileGenerator> imports, Set<FileGenerator> enumImports) {
if (_fieldList == null) throw new StateError("message not resolved");
for (var field in _fieldList) {
var typeGen = field.baseType.generator;
if (typeGen != null && typeGen.fileGen != fileGen) {
// The field's type is defined in a different .pb.dart file.
// Therefore we need to import it.
if (typeGen is EnumGenerator) {
enumImports.add(typeGen.fileGen);
} else if (typeGen != null) {
imports.add(typeGen.fileGen);
}
}
for (var m in _messageGenerators) {
m.addImportsTo(imports);
m.addImportsTo(imports, enumImports);
}
for (var x in _extensionGenerators) {
x.addImportsTo(imports);
x.addImportsTo(imports, enumImports);
}
}

// Returns the number of enums in this message and all nested messages.
int get enumCount {
var count = _enumGenerators.length;
for (var m in _messageGenerators) {
count += m.enumCount;
}
return count;
}

/// Adds dependencies of [generateConstants] to [imports].
///
/// For each .pbjson.dart file that the generated code needs to import,
Expand All @@ -241,10 +251,6 @@ class MessageGenerator extends ProtobufContainer {
_methodNames.addAll(mixin.findReservedNames());
}

for (EnumGenerator e in _enumGenerators) {
e.generate(out);
}

for (MessageGenerator m in _messageGenerators) {
m.generate(out);
}
Expand Down Expand Up @@ -408,6 +414,16 @@ class MessageGenerator extends ProtobufContainer {
}
}

void generateEnums(IndentingWriter out) {
for (EnumGenerator e in _enumGenerators) {
e.generate(out);
}

for (MessageGenerator m in _messageGenerators) {
m.generateEnums(out);
}
}

/// Writes a Dart constant containing the JSON for the ProtoDescriptor.
/// Also writes a separate constant for each nested message,
/// to avoid duplication.
Expand Down
Loading

0 comments on commit f032601

Please sign in to comment.