blob: f877ac6d1b216e98d3e13d08de43e1424e8b5221 [file] [log] [blame]
Jon Skeet60c059b2008-10-23 21:17:56 +01001using System.Collections;
Jon Skeet68036862008-10-22 13:30:34 +01002using Google.ProtocolBuffers.Descriptors;
3using Google.ProtocolBuffers.DescriptorProtos;
4using System.Collections.Generic;
5
6using ExtensionRange = Google.ProtocolBuffers.DescriptorProtos.DescriptorProto.Types.ExtensionRange;
7
8namespace Google.ProtocolBuffers.ProtoGen {
9 internal class MessageGenerator : SourceGeneratorBase<MessageDescriptor>, ISourceGenerator {
10 internal MessageGenerator(MessageDescriptor descriptor) : base(descriptor) {
11 }
12
13 private string ClassName {
14 get { return Descriptor.Name; }
15 }
16
17 private string FullClassName {
Jon Skeetd6343be2008-11-12 23:39:44 +000018 get { return GetClassName(Descriptor); }
Jon Skeet68036862008-10-22 13:30:34 +010019 }
20
21 /// <summary>
22 /// Get an identifier that uniquely identifies this type within the file.
23 /// This is used to declare static variables related to this type at the
24 /// outermost file scope.
25 /// </summary>
26 static string GetUniqueFileScopeIdentifier(IDescriptor descriptor) {
27 return "static_" + descriptor.FullName.Replace(".", "_");
28 }
29
30 internal void GenerateStaticVariables(TextGenerator writer) {
31 // Because descriptor.proto (Google.ProtocolBuffers.DescriptorProtos) is
32 // used in the construction of descriptors, we have a tricky bootstrapping
33 // problem. To help control static initialization order, we make sure all
34 // descriptors and other static data that depends on them are members of
35 // the proto-descriptor class. This way, they will be initialized in
36 // a deterministic order.
37
38 string identifier = GetUniqueFileScopeIdentifier(Descriptor);
39
40 // The descriptor for this type.
Jon Skeetd6343be2008-11-12 23:39:44 +000041 string access = Descriptor.File.CSharpOptions.NestClasses ? "private" : "internal";
Jon Skeet68036862008-10-22 13:30:34 +010042 writer.WriteLine("{0} static readonly pbd::MessageDescriptor internal__{1}__Descriptor", access, identifier);
43 if (Descriptor.ContainingType == null) {
44 writer.WriteLine(" = Descriptor.MessageTypes[{0}];", Descriptor.Index);
45 } else {
46 writer.WriteLine(" = internal__{0}__Descriptor.NestedTypes[{1}];", GetUniqueFileScopeIdentifier(Descriptor.ContainingType), Descriptor.Index);
47 }
48 writer.WriteLine("{0} static pb::FieldAccess.FieldAccessorTable<{1}, {1}.Builder> internal__{2}__FieldAccessorTable",
49 access, FullClassName, identifier);
50 writer.WriteLine(" = new pb::FieldAccess.FieldAccessorTable<{0}, {0}.Builder>(internal__{1}__Descriptor,",
51 FullClassName, identifier);
52 writer.Print(" new string[] { ");
53 foreach (FieldDescriptor field in Descriptor.Fields) {
Jon Skeetd6343be2008-11-12 23:39:44 +000054 writer.Write("\"{0}\", ", NameHelpers.UnderscoresToPascalCase(GetFieldName(field)));
Jon Skeet68036862008-10-22 13:30:34 +010055 }
56 writer.WriteLine("});");
57
58 // Generate static members for all nested types.
59 foreach (MessageDescriptor nestedMessage in Descriptor.NestedTypes) {
60 new MessageGenerator(nestedMessage).GenerateStaticVariables(writer);
61 }
62 }
63
64 public void Generate(TextGenerator writer) {
65 writer.WriteLine("{0} sealed partial class {1} : pb::{2}Message<{1}, {1}.Builder> {{",
66 ClassAccessLevel, ClassName, Descriptor.Proto.ExtensionRangeCount > 0 ? "Extendable" : "Generated");
67 writer.Indent();
68 // Must call BuildPartial() to make sure all lists are made read-only
69 writer.WriteLine("private static readonly {0} defaultInstance = new Builder().BuildPartial();", ClassName);
70 writer.WriteLine("public static {0} DefaultInstance {{", ClassName);
71 writer.WriteLine(" get { return defaultInstance; }");
72 writer.WriteLine("}");
73 writer.WriteLine();
74 writer.WriteLine("public override {0} DefaultInstanceForType {{", ClassName);
75 writer.WriteLine(" get { return defaultInstance; }");
76 writer.WriteLine("}");
77 writer.WriteLine();
78 writer.WriteLine("protected override {0} ThisMessage {{", ClassName);
79 writer.WriteLine(" get { return this; }");
80 writer.WriteLine("}");
81 writer.WriteLine();
82 writer.WriteLine("public static pbd::MessageDescriptor Descriptor {");
Jon Skeetd6343be2008-11-12 23:39:44 +000083 writer.WriteLine(" get {{ return {0}.internal__{1}__Descriptor; }}", DescriptorUtil.GetFullUmbrellaClassName(Descriptor),
Jon Skeet68036862008-10-22 13:30:34 +010084 GetUniqueFileScopeIdentifier(Descriptor));
85 writer.WriteLine("}");
86 writer.WriteLine();
87 writer.WriteLine("protected override pb::FieldAccess.FieldAccessorTable<{0}, {0}.Builder> InternalFieldAccessors {{", ClassName);
Jon Skeetd6343be2008-11-12 23:39:44 +000088 writer.WriteLine(" get {{ return {0}.internal__{1}__FieldAccessorTable; }}", DescriptorUtil.GetFullUmbrellaClassName(Descriptor),
Jon Skeet68036862008-10-22 13:30:34 +010089 GetUniqueFileScopeIdentifier(Descriptor));
90 writer.WriteLine("}");
91 writer.WriteLine();
92
93 // Extensions don't need to go in an extra nested type
94 WriteChildren(writer, null, Descriptor.Extensions);
95
96 if (Descriptor.EnumTypes.Count + Descriptor.NestedTypes.Count > 0) {
97 writer.WriteLine("#region Nested types");
98 writer.WriteLine("public static class Types {");
99 writer.Indent();
100 WriteChildren(writer, null, Descriptor.EnumTypes);
101 WriteChildren(writer, null, Descriptor.NestedTypes);
102 writer.Outdent();
103 writer.WriteLine("}");
104 writer.WriteLine("#endregion");
105 writer.WriteLine();
106 }
107
108 foreach(FieldDescriptor fieldDescriptor in Descriptor.Fields) {
109 // Rats: we lose the debug comment here :(
110 SourceGenerators.CreateFieldGenerator(fieldDescriptor).GenerateMembers(writer);
111 writer.WriteLine();
112 }
113
114 if (Descriptor.File.Options.OptimizeFor == FileOptions.Types.OptimizeMode.SPEED) {
115 GenerateIsInitialized(writer);
116 GenerateMessageSerializationMethods(writer);
117 }
118
119 GenerateParseFromMethods(writer);
120 GenerateBuilder(writer);
121 }
122
123 private void GenerateMessageSerializationMethods(TextGenerator writer) {
124 List<FieldDescriptor> sortedFields = new List<FieldDescriptor>(Descriptor.Fields);
125 sortedFields.Sort((f1, f2) => f1.FieldNumber.CompareTo(f2.FieldNumber));
126
127 List<ExtensionRange> sortedExtensions = new List<ExtensionRange>(Descriptor.Proto.ExtensionRangeList);
128 sortedExtensions.Sort((r1, r2) => (r1.Start.CompareTo(r2.Start)));
129
130 writer.WriteLine("public override void WriteTo(pb::CodedOutputStream output) {");
131 writer.Indent();
132 if (Descriptor.Proto.ExtensionRangeList.Count > 0) {
133 writer.WriteLine("pb::ExtendableMessage<{0}, {0}.Builder>.ExtensionWriter extensionWriter = CreateExtensionWriter(this);",
134 ClassName);
135 }
136
137 // Merge the fields and the extension ranges, both sorted by field number.
138 for (int i = 0, j = 0; i < Descriptor.Fields.Count || j < sortedExtensions.Count; ) {
139 if (i == Descriptor.Fields.Count) {
140 GenerateSerializeOneExtensionRange(writer, sortedExtensions[j++]);
141 } else if (j == sortedExtensions.Count) {
142 GenerateSerializeOneField(writer, sortedFields[i++]);
143 } else if (sortedFields[i].FieldNumber < sortedExtensions[j].Start) {
144 GenerateSerializeOneField(writer, sortedFields[i++]);
145 } else {
146 GenerateSerializeOneExtensionRange(writer, sortedExtensions[j++]);
147 }
148 }
149
150 if (Descriptor.Proto.Options.MessageSetWireFormat) {
151 writer.WriteLine("UnknownFields.WriteAsMessageSetTo(output);");
152 } else {
153 writer.WriteLine("UnknownFields.WriteTo(output);");
154 }
155
156 writer.Outdent();
157 writer.WriteLine("}");
158 writer.WriteLine();
159 writer.WriteLine("private int memoizedSerializedSize = -1;");
160 writer.WriteLine("public override int SerializedSize {");
161 writer.Indent();
162 writer.WriteLine("get {");
163 writer.Indent();
164 writer.WriteLine("int size = memoizedSerializedSize;");
165 writer.WriteLine("if (size != -1) return size;");
166 writer.WriteLine();
167 writer.WriteLine("size = 0;");
168 foreach (FieldDescriptor field in Descriptor.Fields) {
169 SourceGenerators.CreateFieldGenerator(field).GenerateSerializedSizeCode(writer);
170 }
171 if (Descriptor.Proto.ExtensionRangeCount > 0) {
172 writer.WriteLine("size += ExtensionsSerializedSize;");
173 }
174
175 if (Descriptor.Options.MessageSetWireFormat) {
176 writer.WriteLine("size += UnknownFields.SerializedSizeAsMessageSet;");
177 } else {
178 writer.WriteLine("size += UnknownFields.SerializedSize;");
179 }
180 writer.WriteLine("memoizedSerializedSize = size;");
181 writer.WriteLine("return size;");
182 writer.Outdent();
183 writer.WriteLine("}");
184 writer.Outdent();
185 writer.WriteLine("}");
186 writer.WriteLine();
187 }
188
Jon Skeetd6343be2008-11-12 23:39:44 +0000189 private void GenerateSerializeOneField(TextGenerator writer, FieldDescriptor fieldDescriptor) {
Jon Skeet68036862008-10-22 13:30:34 +0100190 SourceGenerators.CreateFieldGenerator(fieldDescriptor).GenerateSerializationCode(writer);
191 }
192
193 private static void GenerateSerializeOneExtensionRange(TextGenerator writer, ExtensionRange extensionRange) {
194 writer.WriteLine("extensionWriter.WriteUntil({0}, output);", extensionRange.End);
195 }
196
197 private void GenerateParseFromMethods(TextGenerator writer) {
198 // Note: These are separate from GenerateMessageSerializationMethods()
199 // because they need to be generated even for messages that are optimized
200 // for code size.
201
202 writer.WriteLine("public static {0} ParseFrom(pb::ByteString data) {{", ClassName);
203 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();");
204 writer.WriteLine("}");
205 writer.WriteLine("public static {0} ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) {{", ClassName);
206 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();");
207 writer.WriteLine("}");
208 writer.WriteLine("public static {0} ParseFrom(byte[] data) {{", ClassName);
209 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();");
210 writer.WriteLine("}");
211 writer.WriteLine("public static {0} ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) {{", ClassName);
212 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();");
213 writer.WriteLine("}");
214 writer.WriteLine("public static {0} ParseFrom(global::System.IO.Stream input) {{", ClassName);
215 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();");
216 writer.WriteLine("}");
217 writer.WriteLine("public static {0} ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {{", ClassName);
218 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();");
219 writer.WriteLine("}");
220 writer.WriteLine("public static {0} ParseFrom(pb::CodedInputStream input) {{", ClassName);
221 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();");
222 writer.WriteLine("}");
223 writer.WriteLine("public static {0} ParseFrom(pb::CodedInputStream input, pb::ExtensionRegistry extensionRegistry) {{", ClassName);
224 writer.WriteLine(" return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();");
225 writer.WriteLine("}");
226 }
227
228 /// <summary>
229 /// Returns whether or not the specified message type has any required fields.
230 /// If it doesn't, calls to check for initialization can be optimised.
231 /// TODO(jonskeet): Move this into MessageDescriptor?
232 /// </summary>
233 private static bool HasRequiredFields(MessageDescriptor descriptor, Dictionary<MessageDescriptor,object> alreadySeen) {
234 if (alreadySeen.ContainsKey(descriptor)) {
235 // The type is already in cache. This means that either:
236 // a. The type has no required fields.
237 // b. We are in the midst of checking if the type has required fields,
238 // somewhere up the stack. In this case, we know that if the type
239 // has any required fields, they'll be found when we return to it,
240 // and the whole call to HasRequiredFields() will return true.
241 // Therefore, we don't have to check if this type has required fields
242 // here.
243 return false;
244 }
245 alreadySeen[descriptor] = descriptor; // Value is irrelevant
246
247 // If the type has extensions, an extension with message type could contain
248 // required fields, so we have to be conservative and assume such an
249 // extension exists.
250 if (descriptor.Extensions.Count > 0) {
251 return true;
252 }
253
254 foreach (FieldDescriptor field in descriptor.Fields) {
255 if (field.IsRequired) {
256 return true;
257 }
258 // Message or group
259 if (field.MappedType == MappedType.Message) {
260 if (HasRequiredFields(field.MessageType, alreadySeen)) {
261 return true;
262 }
263 }
264 }
265 return false;
266 }
267
268 private void GenerateBuilder(TextGenerator writer) {
269 writer.WriteLine("public static Builder CreateBuilder() { return new Builder(); }");
270 writer.WriteLine("public override Builder CreateBuilderForType() { return new Builder(); }");
271 writer.WriteLine("public static Builder CreateBuilder({0} prototype) {{", ClassName);
272 writer.WriteLine(" return (Builder) new Builder().MergeFrom(prototype);");
273 writer.WriteLine("}");
274 writer.WriteLine();
275 writer.WriteLine("{0} sealed partial class Builder : pb::{2}Builder<{1}, Builder> {{",
276 ClassAccessLevel, ClassName, Descriptor.Proto.ExtensionRangeCount > 0 ? "Extendable" : "Generated");
277 writer.Indent();
278 writer.WriteLine("protected override Builder ThisBuilder {");
279 writer.WriteLine(" get { return this; }");
280 writer.WriteLine("}");
281 GenerateCommonBuilderMethods(writer);
282 if (Descriptor.File.Options.OptimizeFor == FileOptions.Types.OptimizeMode.SPEED) {
283 GenerateBuilderParsingMethods(writer);
284 }
285 foreach (FieldDescriptor field in Descriptor.Fields) {
286 writer.WriteLine();
287 // No field comment :(
288 SourceGenerators.CreateFieldGenerator(field).GenerateBuilderMembers(writer);
289 }
290 writer.Outdent();
291 writer.WriteLine("}");
292 writer.Outdent();
293 writer.WriteLine("}");
294 writer.WriteLine();
295 }
296
297 private void GenerateCommonBuilderMethods(TextGenerator writer) {
298 writer.WriteLine("{0} Builder() {{}}", ClassAccessLevel);
299 writer.WriteLine();
300 writer.WriteLine("{0} result = new {0}();", ClassName);
301 writer.WriteLine();
302 writer.WriteLine("protected override {0} MessageBeingBuilt {{", ClassName);
303 writer.WriteLine(" get { return result; }");
304 writer.WriteLine("}");
305 writer.WriteLine();
306 writer.WriteLine("public override Builder Clear() {");
307 writer.WriteLine(" result = new {0}();", ClassName);
308 writer.WriteLine(" return this;");
309 writer.WriteLine("}");
310 writer.WriteLine();
311 writer.WriteLine("public override Builder Clone() {");
312 writer.WriteLine(" return new Builder().MergeFrom(result);");
313 writer.WriteLine("}");
314 writer.WriteLine();
315 writer.WriteLine("public override pbd::MessageDescriptor DescriptorForType {");
316 writer.WriteLine(" get {{ return {0}.Descriptor; }}", ClassName);
317 writer.WriteLine("}");
318 writer.WriteLine();
319 writer.WriteLine("public override {0} DefaultInstanceForType {{", ClassName);
320 writer.WriteLine(" get {{ return {0}.DefaultInstance; }}", ClassName);
321 writer.WriteLine("}");
322 writer.WriteLine();
323
324 writer.WriteLine("public override {0} BuildPartial() {{", ClassName);
325 writer.Indent();
326 foreach (FieldDescriptor field in Descriptor.Fields) {
327 SourceGenerators.CreateFieldGenerator(field).GenerateBuildingCode(writer);
328 }
329 writer.WriteLine("{0} returnMe = result;", ClassName);
330 writer.WriteLine("result = null;");
331 writer.WriteLine("return returnMe;");
332 writer.Outdent();
333 writer.WriteLine("}");
334 writer.WriteLine();
335
336 if (Descriptor.File.Options.OptimizeFor == FileOptions.Types.OptimizeMode.SPEED) {
337 writer.WriteLine("public override Builder MergeFrom(pb::IMessage other) {");
338 writer.WriteLine(" if (other is {0}) {{", ClassName);
339 writer.WriteLine(" return MergeFrom(({0}) other);", ClassName);
340 writer.WriteLine(" } else {");
341 writer.WriteLine(" base.MergeFrom(other);");
342 writer.WriteLine(" return this;");
343 writer.WriteLine(" }");
344 writer.WriteLine("}");
345 writer.WriteLine();
346 writer.WriteLine("public override Builder MergeFrom({0} other) {{", ClassName);
347 // Optimization: If other is the default instance, we know none of its
348 // fields are set so we can skip the merge.
349 writer.Indent();
350 writer.WriteLine("if (other == {0}.DefaultInstance) return this;", ClassName);
351 foreach (FieldDescriptor field in Descriptor.Fields) {
352 SourceGenerators.CreateFieldGenerator(field).GenerateMergingCode(writer);
353 }
354 writer.WriteLine("this.MergeUnknownFields(other.UnknownFields);");
355 writer.WriteLine("return this;");
356 writer.Outdent();
357 writer.WriteLine("}");
358 writer.WriteLine();
359 }
360 }
361
362 private void GenerateBuilderParsingMethods(TextGenerator writer) {
363 List<FieldDescriptor> sortedFields = new List<FieldDescriptor>(Descriptor.Fields);
364 sortedFields.Sort((f1, f2) => f1.FieldNumber.CompareTo(f2.FieldNumber));
365
366 writer.WriteLine("public override Builder MergeFrom(pb::CodedInputStream input) {");
367 writer.WriteLine(" return MergeFrom(input, pb::ExtensionRegistry.Empty);");
368 writer.WriteLine("}");
369 writer.WriteLine();
370 writer.WriteLine("public override Builder MergeFrom(pb::CodedInputStream input, pb::ExtensionRegistry extensionRegistry) {");
371 writer.Indent();
372 writer.WriteLine("pb::UnknownFieldSet.Builder unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);");
373 writer.WriteLine("while (true) {");
374 writer.Indent();
375 writer.WriteLine("uint tag = input.ReadTag();");
376 writer.WriteLine("switch (tag) {");
377 writer.Indent();
378 writer.WriteLine("case 0: {"); // 0 signals EOF / limit reached
379 writer.WriteLine(" this.UnknownFields = unknownFields.Build();");
380 writer.WriteLine(" return this;");
381 writer.WriteLine("}");
382 writer.WriteLine("default: {");
383 writer.WriteLine(" if (!ParseUnknownField(input, unknownFields, extensionRegistry, tag)) {");
384 writer.WriteLine(" this.UnknownFields = unknownFields.Build();");
385 writer.WriteLine(" return this;"); // it's an endgroup tag
386 writer.WriteLine(" }");
387 writer.WriteLine(" break;");
388 writer.WriteLine("}");
389 foreach (FieldDescriptor field in sortedFields) {
390 uint tag = WireFormat.MakeTag(field.FieldNumber, WireFormat.GetWireType(field.FieldType));
391 writer.WriteLine("case {0}: {{", tag);
392 writer.Indent();
393 SourceGenerators.CreateFieldGenerator(field).GenerateParsingCode(writer);
394 writer.WriteLine("break;");
395 writer.Outdent();
396 writer.WriteLine("}");
397 }
398 writer.Outdent();
399 writer.WriteLine("}");
400 writer.Outdent();
401 writer.WriteLine("}");
402 writer.Outdent();
403 writer.WriteLine("}");
404 writer.WriteLine();
405 }
406
407 private void GenerateIsInitialized(TextGenerator writer) {
408 writer.WriteLine("public override bool IsInitialized {");
409 writer.Indent();
410 writer.WriteLine("get {");
411 writer.Indent();
412
413 // Check that all required fields in this message are set.
414 // TODO(kenton): We can optimize this when we switch to putting all the
415 // "has" fields into a single bitfield.
416 foreach (FieldDescriptor field in Descriptor.Fields) {
417 if (field.IsRequired) {
Jon Skeetd6343be2008-11-12 23:39:44 +0000418 writer.WriteLine("if (!has{0}) return false;", NameHelpers.UnderscoresToPascalCase(field.Name));
Jon Skeet68036862008-10-22 13:30:34 +0100419 }
420 }
421
422 // Now check that all embedded messages are initialized.
423 foreach (FieldDescriptor field in Descriptor.Fields) {
424 if (field.FieldType != FieldType.Message ||
425 !HasRequiredFields(field.MessageType, new Dictionary<MessageDescriptor, object>())) {
426 continue;
427 }
Jon Skeetd6343be2008-11-12 23:39:44 +0000428 string propertyName = NameHelpers.UnderscoresToPascalCase(GetFieldName(field));
Jon Skeet68036862008-10-22 13:30:34 +0100429 if (field.IsRepeated) {
Jon Skeetd6343be2008-11-12 23:39:44 +0000430 writer.WriteLine("foreach ({0} element in {1}List) {{", GetClassName(field.MessageType), propertyName);
Jon Skeet68036862008-10-22 13:30:34 +0100431 writer.WriteLine(" if (!element.IsInitialized) return false;");
432 writer.WriteLine("}");
433 } else if (field.IsOptional) {
434 writer.WriteLine("if (Has{0}) {{", propertyName);
435 writer.WriteLine(" if (!{0}.IsInitialized) return false;", propertyName);
436 writer.WriteLine("}");
437 } else {
438 writer.WriteLine("if (!{0}.IsInitialized) return false;", propertyName);
439 }
440 }
441
442 if (Descriptor.Proto.ExtensionRangeCount > 0) {
443 writer.WriteLine("if (!ExtensionsAreInitialized) return false;");
444 }
445 writer.WriteLine("return true;");
446 writer.Outdent();
447 writer.WriteLine("}");
448 writer.Outdent();
449 writer.WriteLine("}");
450 writer.WriteLine();
451 }
452 }
453}