Просмотр исходного кода

goprotobuf: Public import getters.

R=r
CC=golang-dev
http://codereview.appspot.com/6525044
David Symonds 13 лет назад
Родитель
Сommit
381349d798

+ 1 - 1
proto/encode.go

@@ -56,7 +56,7 @@ func (e *ErrRequiredNotSet) Error() string {
 var (
 	// ErrRepeatedHasNil is the error returned if Marshal is called with
 	// a struct with a repeated field containing a nil element.
-	ErrRepeatedHasNil = errors.New("proto: repeated field has nil")
+	ErrRepeatedHasNil = errors.New("proto: repeated field has nil element")
 
 	// ErrNil is the error returned if Marshal is called with nil.
 	ErrNil = errors.New("proto: Marshal called with nil")

+ 61 - 4
protoc-gen-go/generator/generator.go

@@ -266,9 +266,17 @@ type Symbol interface {
 type messageSymbol struct {
 	sym                         string
 	hasExtensions, isMessageSet bool
+	getters                     []getterSymbol
 }
 
-func (ms messageSymbol) GenerateAlias(g *Generator, pkg string) {
+type getterSymbol struct {
+	name     string
+	typ      string
+	typeName string // canonical name in proto world; empty for proto.Message and similar
+	genType  bool   // whether typ is a generated type (message/group/enum)
+}
+
+func (ms *messageSymbol) GenerateAlias(g *Generator, pkg string) {
 	remoteSym := pkg + "." + ms.sym
 
 	g.P("type ", ms.sym, " ", remoteSym)
@@ -287,6 +295,26 @@ func (ms messageSymbol) GenerateAlias(g *Generator, pkg string) {
 				"{ return (*", remoteSym, ")(this).Unmarshal(buf) }")
 		}
 	}
+	for _, get := range ms.getters {
+		typ := get.typ
+		val := "(*" + remoteSym + ")(this)." + get.name + "()"
+		if get.genType {
+			// typ will be "*pkg.T" (message/group) or "pkg.T" (enum).
+			// Drop the package qualifier since we have hoisted the type into this package.
+			star := typ[0] == '*'
+			typ = typ[strings.Index(typ, ".")+1:]
+			if star {
+				typ = "*" + typ
+			}
+			// Convert imported type into the forwarding type.
+			val = "(" + typ + ")(" + val + ")"
+		}
+
+		g.P("func (this *", ms.sym, ") ", get.name, "() ", typ, " { return ", val, " }")
+		if get.typeName != "" {
+			g.RecordTypeUse(get.typeName)
+		}
+	}
 }
 
 type enumSymbol string
@@ -1345,8 +1373,6 @@ func (g *Generator) generateMessage(message *Descriptor) {
 		g.P("}")
 	}
 
-	g.file.addExport(message, messageSymbol{ccTypeName, hasExtensions, isMessageSet})
-
 	// Default constants
 	defNames := make(map[*descriptor.FieldDescriptorProto]string)
 	for _, field := range message.Field {
@@ -1398,7 +1424,7 @@ func (g *Generator) generateMessage(message *Descriptor) {
 	g.P()
 
 	// Field getters
-	// TODO: Generate getters for publicly imported aliases, if required.
+	var getters []getterSymbol
 	for _, field := range message.Field {
 		if isRepeated(field) {
 			continue
@@ -1411,6 +1437,35 @@ func (g *Generator) generateMessage(message *Descriptor) {
 			typename = typename[1:]
 			star = "*"
 		}
+
+		// Only export getter symbols for basic types,
+		// and for messages, groups and enums in the same package.
+		// Foreign types can't be hoisted through a public import because
+		// the importer may not already be importing the defining .proto.
+		// As an example, imagine we have an import tree like this:
+		//   A.proto -> B.proto -> C.proto
+		// If A publicly imports B, we need to generate the getters from B in A's output,
+		// but if one such getter returns something from C then we cannot do that
+		// because A is not importing C already.
+		var getter, genType bool
+		switch *field.Type {
+		case descriptor.FieldDescriptorProto_TYPE_GROUP, descriptor.FieldDescriptorProto_TYPE_MESSAGE,
+			descriptor.FieldDescriptorProto_TYPE_ENUM:
+			// Only export getter if its return type is in this package.
+			getter = g.ObjectNamed(field.GetTypeName()).PackageName() == message.PackageName()
+			genType = true
+		default:
+			getter = true
+		}
+		if getter {
+			getters = append(getters, getterSymbol{
+				name:     mname,
+				typ:      typename,
+				typeName: field.GetTypeName(),
+				genType:  genType,
+			})
+		}
+
 		g.P("func (this *", ccTypeName, ") "+mname+"() "+typename+" {")
 		g.In()
 		def, hasDef := defNames[field]
@@ -1463,6 +1518,8 @@ func (g *Generator) generateMessage(message *Descriptor) {
 		g.P()
 	}
 
+	g.file.addExport(message, &messageSymbol{ccTypeName, hasExtensions, isMessageSet, getters})
+
 	for _, ext := range message.ext {
 		g.generateExtension(ext)
 	}

+ 9 - 0
protoc-gen-go/testdata/imp.proto

@@ -32,10 +32,16 @@
 package imp;
 
 import "imp2.proto";
+import "imp3.proto";
 
 message ImportedMessage {
   required int64 field = 1;
 
+  // The forwarded getters for these fields are fiddly to get right.
+  optional ImportedMessage2 local_msg = 2;
+  optional ForeignImportedMessage foreign_msg = 3;  // in imp3.proto
+  optional Owner enum_field = 4;
+
   enum Owner {
     DAVE = 1;
     MIKE = 2;
@@ -44,6 +50,9 @@ message ImportedMessage {
   extensions 90 to 100;
 }
 
+message ImportedMessage2 {
+}
+
 message ImportedExtendable {
   option message_set_wire_format = true;
   extensions 100 to max;

+ 35 - 0
protoc-gen-go/testdata/imp3.proto

@@ -0,0 +1,35 @@
+//
+// Copyright 2012 Google Inc.  All rights reserved.
+// http://code.google.com/p/goprotobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package imp;
+
+message ForeignImportedMessage {
+  optional string tuber = 1;
+}