OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 part of protoc; | 5 part of protoc; |
6 | 6 |
| 7 final _dartIdentifier = new RegExp(r'^\w+$'); |
| 8 |
7 /// Generates the Dart output files for one .proto input file. | 9 /// Generates the Dart output files for one .proto input file. |
8 /// | 10 /// |
9 /// Outputs include .pb.dart, pbenum.dart, and .pbjson.dart. | 11 /// Outputs include .pb.dart, pbenum.dart, and .pbjson.dart. |
10 class FileGenerator extends ProtobufContainer { | 12 class FileGenerator extends ProtobufContainer { |
11 /// Returns the the mixin to use by default in this file, | 13 /// Reads and the declared mixins in the file, keyed by name. |
12 /// or null for no mixin by default. | 14 /// |
13 static PbMixin _getDefaultMixin(FileDescriptorProto desc) { | 15 /// Performs some basic validation on declared mixins, e.g. whether names |
14 if (!desc.hasOptions()) return null; | 16 /// are valid dart identifiers and whether there are cycles in the `parent` |
15 if (!desc.options.hasExtension(Dart_options.defaultMixin)) { | 17 /// hierarchy. |
16 return null; | 18 /// Does not check for existence of import files or classes. |
| 19 static Map<String, PbMixin> _getDeclaredMixins(FileDescriptorProto desc) { |
| 20 String mixinError(String error) => |
| 21 'Option "mixins" in file ${desc.name}: $error'; |
| 22 |
| 23 if (!desc.hasOptions() || |
| 24 !desc.options.hasExtension(Dart_options.importedMixins)) { |
| 25 return <String, PbMixin>{}; |
17 } | 26 } |
18 var name = desc.options.getExtension(Dart_options.defaultMixin); | 27 var dartMixins = <String, DartMixin>{}; |
19 PbMixin mixin = findMixin(name); | 28 ImportedMixins importedMixins = |
20 if (mixin == null) { | 29 desc.options.getExtension(Dart_options.importedMixins); |
21 throw ("unknown mixin class: ${name}"); | 30 for (DartMixin mixin in importedMixins.mixins) { |
| 31 if (dartMixins.containsKey(mixin.name)) { |
| 32 throw mixinError('Duplicate mixin name: "${mixin.name}"'); |
| 33 } |
| 34 if (!mixin.name.startsWith(_dartIdentifier)) { |
| 35 throw mixinError( |
| 36 '"${mixin.name}" is not a valid dart class identifier'); |
| 37 } |
| 38 if (mixin.hasParent() && !mixin.parent.startsWith(_dartIdentifier)) { |
| 39 throw mixinError('Mixin parent "${mixin.parent}" of "${mixin.name}" is ' |
| 40 'not a valid dart class identifier'); |
| 41 } |
| 42 dartMixins[mixin.name] = mixin; |
22 } | 43 } |
23 return mixin; | 44 |
| 45 // Detect cycles and unknown parents. |
| 46 for (var mixin in dartMixins.values) { |
| 47 if (!mixin.hasParent()) continue; |
| 48 var currentMixin = mixin; |
| 49 var parentChain = <String>[]; |
| 50 while (currentMixin.hasParent()) { |
| 51 var parentName = currentMixin.parent; |
| 52 |
| 53 bool declaredMixin = dartMixins.containsKey(parentName); |
| 54 bool internalMixin = !declaredMixin && findMixin(parentName) != null; |
| 55 |
| 56 if (internalMixin) break; // No further validation of parent chain. |
| 57 |
| 58 if (!declaredMixin) { |
| 59 throw mixinError('Unknown mixin parent "${mixin.parent}" of ' |
| 60 '"${currentMixin.name}"'); |
| 61 } |
| 62 |
| 63 if (parentChain.contains(parentName)) { |
| 64 var cycle = parentChain.join('->') + '->$parentName'; |
| 65 throw mixinError('Cycle in parent chain: $cycle'); |
| 66 } |
| 67 parentChain.add(parentName); |
| 68 currentMixin = dartMixins[parentName]; |
| 69 } |
| 70 } |
| 71 |
| 72 // Turn DartMixins into PbMixins. |
| 73 final pbMixins = <String, PbMixin>{}; |
| 74 PbMixin resolveMixin(String name) { |
| 75 if (pbMixins.containsKey(name)) return pbMixins[name]; |
| 76 if (dartMixins.containsKey(name)) { |
| 77 var dartMixin = dartMixins[name]; |
| 78 var pbMixin = new PbMixin(dartMixin.name, |
| 79 importFrom: dartMixin.importFrom, |
| 80 parent: resolveMixin(dartMixin.parent)); |
| 81 pbMixins[name] = pbMixin; |
| 82 return pbMixin; |
| 83 } |
| 84 return findMixin(name); |
| 85 } |
| 86 for (var mixin in dartMixins.values) { |
| 87 resolveMixin(mixin.name); |
| 88 } |
| 89 return pbMixins; |
24 } | 90 } |
25 | 91 |
26 final FileDescriptorProto _fileDescriptor; | 92 final FileDescriptorProto _fileDescriptor; |
27 | 93 |
28 // The relative path used to import the .proto file, as a URI. | 94 // The relative path used to import the .proto file, as a URI. |
29 final Uri protoFileUri; | 95 final Uri protoFileUri; |
30 | 96 |
31 final List<EnumGenerator> enumGenerators = <EnumGenerator>[]; | 97 final List<EnumGenerator> enumGenerators = <EnumGenerator>[]; |
32 final List<MessageGenerator> messageGenerators = <MessageGenerator>[]; | 98 final List<MessageGenerator> messageGenerators = <MessageGenerator>[]; |
33 final List<ExtensionGenerator> extensionGenerators = <ExtensionGenerator>[]; | 99 final List<ExtensionGenerator> extensionGenerators = <ExtensionGenerator>[]; |
34 final List<ClientApiGenerator> clientApiGenerators = <ClientApiGenerator>[]; | 100 final List<ClientApiGenerator> clientApiGenerators = <ClientApiGenerator>[]; |
35 final List<ServiceGenerator> serviceGenerators = <ServiceGenerator>[]; | 101 final List<ServiceGenerator> serviceGenerators = <ServiceGenerator>[]; |
36 | 102 |
37 /// True if cross-references have been resolved. | 103 /// True if cross-references have been resolved. |
38 bool _linked = false; | 104 bool _linked = false; |
39 | 105 |
40 FileGenerator(FileDescriptorProto descriptor) | 106 FileGenerator(FileDescriptorProto descriptor) |
41 : _fileDescriptor = descriptor, | 107 : _fileDescriptor = descriptor, |
42 protoFileUri = new Uri.file(descriptor.name) { | 108 protoFileUri = new Uri.file(descriptor.name) { |
43 if (protoFileUri.isAbsolute) { | 109 if (protoFileUri.isAbsolute) { |
44 // protoc should never generate an import with an absolute path. | 110 // protoc should never generate an import with an absolute path. |
45 throw "FAILURE: Import with absolute path is not supported"; | 111 throw "FAILURE: Import with absolute path is not supported"; |
46 } | 112 } |
47 | 113 |
48 var defaultMixin = _getDefaultMixin(_fileDescriptor); | 114 var declaredMixins = _getDeclaredMixins(descriptor); |
| 115 var defaultMixinName = |
| 116 descriptor.options?.getExtension(Dart_options.defaultMixin) ?? ''; |
| 117 var defaultMixin = |
| 118 declaredMixins[defaultMixinName] ?? findMixin(defaultMixinName); |
| 119 if (defaultMixin == null && defaultMixinName.isNotEmpty) { |
| 120 throw ('Option default_mixin on file ${descriptor.name}: Unknown mixin ' |
| 121 '$defaultMixinName'); |
| 122 } |
49 | 123 |
50 // Load and register all enum and message types. | 124 // Load and register all enum and message types. |
51 for (EnumDescriptorProto enumType in _fileDescriptor.enumType) { | 125 for (EnumDescriptorProto enumType in _fileDescriptor.enumType) { |
52 enumGenerators.add(new EnumGenerator(enumType, this)); | 126 enumGenerators.add(new EnumGenerator(enumType, this)); |
53 } | 127 } |
54 for (DescriptorProto messageType in _fileDescriptor.messageType) { | 128 for (DescriptorProto messageType in _fileDescriptor.messageType) { |
55 messageGenerators | 129 messageGenerators.add(new MessageGenerator( |
56 .add(new MessageGenerator(messageType, this, defaultMixin)); | 130 messageType, this, declaredMixins, defaultMixin)); |
57 } | 131 } |
58 for (FieldDescriptorProto extension in _fileDescriptor.extension) { | 132 for (FieldDescriptorProto extension in _fileDescriptor.extension) { |
59 extensionGenerators.add(new ExtensionGenerator(extension, this)); | 133 extensionGenerators.add(new ExtensionGenerator(extension, this)); |
60 } | 134 } |
61 for (ServiceDescriptorProto service in _fileDescriptor.service) { | 135 for (ServiceDescriptorProto service in _fileDescriptor.service) { |
62 var serviceGen = new ServiceGenerator(service, this); | 136 var serviceGen = new ServiceGenerator(service, this); |
63 serviceGenerators.add(serviceGen); | 137 serviceGenerators.add(serviceGen); |
64 clientApiGenerators.add(new ClientApiGenerator(serviceGen)); | 138 clientApiGenerators.add(new ClientApiGenerator(serviceGen)); |
65 } | 139 } |
66 } | 140 } |
(...skipping 348 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
415 FileGenerator target, String extension) { | 489 FileGenerator target, String extension) { |
416 Uri resolvedImport = | 490 Uri resolvedImport = |
417 config.resolveImport(target.protoFileUri, protoFileUri, extension); | 491 config.resolveImport(target.protoFileUri, protoFileUri, extension); |
418 out.print("import '$resolvedImport'"); | 492 out.print("import '$resolvedImport'"); |
419 if (package != target.package && target.package.isNotEmpty) { | 493 if (package != target.package && target.package.isNotEmpty) { |
420 out.print(' as ${target.packageImportPrefix}'); | 494 out.print(' as ${target.packageImportPrefix}'); |
421 } | 495 } |
422 out.println(';'); | 496 out.println(';'); |
423 } | 497 } |
424 } | 498 } |
OLD | NEW |