github.com/bakjos/protoreflect@v1.9.2/desc/protoparse/source_code_info.go (about)

     1  package protoparse
     2  
     3  import (
     4  	"bytes"
     5  	"strings"
     6  
     7  	"github.com/golang/protobuf/proto"
     8  	dpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
     9  
    10  	"github.com/bakjos/protoreflect/desc/internal"
    11  	"github.com/bakjos/protoreflect/desc/protoparse/ast"
    12  )
    13  
    14  func (r *parseResult) generateSourceCodeInfo() *dpb.SourceCodeInfo {
    15  	if r.nodes == nil {
    16  		// skip files that do not have AST info (these will be files
    17  		// that came from well-known descriptors, instead of from source)
    18  		return nil
    19  	}
    20  
    21  	sci := sourceCodeInfo{commentsUsed: map[*ast.Comment]struct{}{}}
    22  	path := make([]int32, 0, 10)
    23  
    24  	fn := r.getFileNode(r.fd).(*ast.FileNode)
    25  	sci.newLocWithoutComments(fn, nil)
    26  
    27  	if fn.Syntax != nil {
    28  		sci.newLoc(fn.Syntax, append(path, internal.File_syntaxTag))
    29  	}
    30  
    31  	var depIndex, optIndex, msgIndex, enumIndex, extendIndex, svcIndex int32
    32  
    33  	for _, child := range fn.Decls {
    34  		switch child := child.(type) {
    35  		case *ast.ImportNode:
    36  			sci.newLoc(child, append(path, internal.File_dependencyTag, int32(depIndex)))
    37  			depIndex++
    38  		case *ast.PackageNode:
    39  			sci.newLoc(child, append(path, internal.File_packageTag))
    40  		case *ast.OptionNode:
    41  			r.generateSourceCodeInfoForOption(&sci, child, false, &optIndex, append(path, internal.File_optionsTag))
    42  		case *ast.MessageNode:
    43  			r.generateSourceCodeInfoForMessage(&sci, child, nil, append(path, internal.File_messagesTag, msgIndex))
    44  			msgIndex++
    45  		case *ast.EnumNode:
    46  			r.generateSourceCodeInfoForEnum(&sci, child, append(path, internal.File_enumsTag, enumIndex))
    47  			enumIndex++
    48  		case *ast.ExtendNode:
    49  			r.generateSourceCodeInfoForExtensions(&sci, child, &extendIndex, &msgIndex, append(path, internal.File_extensionsTag), append(dup(path), internal.File_messagesTag))
    50  		case *ast.ServiceNode:
    51  			r.generateSourceCodeInfoForService(&sci, child, append(path, internal.File_servicesTag, svcIndex))
    52  			svcIndex++
    53  		}
    54  	}
    55  
    56  	return &dpb.SourceCodeInfo{Location: sci.locs}
    57  }
    58  
    59  func (r *parseResult) generateSourceCodeInfoForOption(sci *sourceCodeInfo, n *ast.OptionNode, compact bool, uninterpIndex *int32, path []int32) {
    60  	if !compact {
    61  		sci.newLocWithoutComments(n, path)
    62  	}
    63  	subPath := r.interpretedOptions[n]
    64  	if len(subPath) > 0 {
    65  		p := path
    66  		if subPath[0] == -1 {
    67  			// used by "default" and "json_name" field pseudo-options
    68  			// to attribute path to parent element (since those are
    69  			// stored directly on the descriptor, not its options)
    70  			p = make([]int32, len(path)-1)
    71  			copy(p, path)
    72  			subPath = subPath[1:]
    73  		}
    74  		sci.newLoc(n, append(p, subPath...))
    75  		return
    76  	}
    77  
    78  	// it's an uninterpreted option
    79  	optPath := append(path, internal.UninterpretedOptionsTag, *uninterpIndex)
    80  	*uninterpIndex++
    81  	sci.newLoc(n, optPath)
    82  	var valTag int32
    83  	switch n.Val.(type) {
    84  	case ast.IdentValueNode:
    85  		valTag = internal.Uninterpreted_identTag
    86  	case *ast.NegativeIntLiteralNode:
    87  		valTag = internal.Uninterpreted_negIntTag
    88  	case ast.IntValueNode:
    89  		valTag = internal.Uninterpreted_posIntTag
    90  	case ast.FloatValueNode:
    91  		valTag = internal.Uninterpreted_doubleTag
    92  	case ast.StringValueNode:
    93  		valTag = internal.Uninterpreted_stringTag
    94  	case *ast.MessageLiteralNode:
    95  		valTag = internal.Uninterpreted_aggregateTag
    96  	}
    97  	if valTag != 0 {
    98  		sci.newLoc(n.Val, append(optPath, valTag))
    99  	}
   100  	for j, nn := range n.Name.Parts {
   101  		optNmPath := append(optPath, internal.Uninterpreted_nameTag, int32(j))
   102  		sci.newLoc(nn, optNmPath)
   103  		sci.newLoc(nn.Name, append(optNmPath, internal.UninterpretedName_nameTag))
   104  	}
   105  }
   106  
   107  func (r *parseResult) generateSourceCodeInfoForMessage(sci *sourceCodeInfo, n ast.MessageDeclNode, fieldPath []int32, path []int32) {
   108  	sci.newLoc(n, path)
   109  
   110  	var decls []ast.MessageElement
   111  	switch n := n.(type) {
   112  	case *ast.MessageNode:
   113  		decls = n.Decls
   114  	case *ast.GroupNode:
   115  		decls = n.Decls
   116  	case *ast.MapFieldNode:
   117  		// map entry so nothing else to do
   118  		return
   119  	}
   120  
   121  	sci.newLoc(n.MessageName(), append(path, internal.Message_nameTag))
   122  	// matching protoc, which emits the corresponding field type name (for group fields)
   123  	// right after the source location for the group message name
   124  	if fieldPath != nil {
   125  		sci.newLoc(n.MessageName(), append(fieldPath, internal.Field_typeNameTag))
   126  	}
   127  
   128  	var optIndex, fieldIndex, oneOfIndex, extendIndex, nestedMsgIndex int32
   129  	var nestedEnumIndex, extRangeIndex, reservedRangeIndex, reservedNameIndex int32
   130  	for _, child := range decls {
   131  		switch child := child.(type) {
   132  		case *ast.OptionNode:
   133  			r.generateSourceCodeInfoForOption(sci, child, false, &optIndex, append(path, internal.Message_optionsTag))
   134  		case *ast.FieldNode:
   135  			r.generateSourceCodeInfoForField(sci, child, append(path, internal.Message_fieldsTag, fieldIndex))
   136  			fieldIndex++
   137  		case *ast.GroupNode:
   138  			fldPath := append(path, internal.Message_fieldsTag, fieldIndex)
   139  			r.generateSourceCodeInfoForField(sci, child, fldPath)
   140  			fieldIndex++
   141  			r.generateSourceCodeInfoForMessage(sci, child, fldPath, append(dup(path), internal.Message_nestedMessagesTag, nestedMsgIndex))
   142  			nestedMsgIndex++
   143  		case *ast.MapFieldNode:
   144  			r.generateSourceCodeInfoForField(sci, child, append(path, internal.Message_fieldsTag, fieldIndex))
   145  			fieldIndex++
   146  			nestedMsgIndex++
   147  		case *ast.OneOfNode:
   148  			r.generateSourceCodeInfoForOneOf(sci, child, &fieldIndex, &nestedMsgIndex, append(path, internal.Message_fieldsTag), append(dup(path), internal.Message_nestedMessagesTag), append(dup(path), internal.Message_oneOfsTag, oneOfIndex))
   149  			oneOfIndex++
   150  		case *ast.MessageNode:
   151  			r.generateSourceCodeInfoForMessage(sci, child, nil, append(path, internal.Message_nestedMessagesTag, nestedMsgIndex))
   152  			nestedMsgIndex++
   153  		case *ast.EnumNode:
   154  			r.generateSourceCodeInfoForEnum(sci, child, append(path, internal.Message_enumsTag, nestedEnumIndex))
   155  			nestedEnumIndex++
   156  		case *ast.ExtendNode:
   157  			r.generateSourceCodeInfoForExtensions(sci, child, &extendIndex, &nestedMsgIndex, append(path, internal.Message_extensionsTag), append(dup(path), internal.Message_nestedMessagesTag))
   158  		case *ast.ExtensionRangeNode:
   159  			r.generateSourceCodeInfoForExtensionRanges(sci, child, &extRangeIndex, append(path, internal.Message_extensionRangeTag))
   160  		case *ast.ReservedNode:
   161  			if len(child.Names) > 0 {
   162  				resPath := append(path, internal.Message_reservedNameTag)
   163  				sci.newLoc(child, resPath)
   164  				for _, rn := range child.Names {
   165  					sci.newLoc(rn, append(resPath, reservedNameIndex))
   166  					reservedNameIndex++
   167  				}
   168  			}
   169  			if len(child.Ranges) > 0 {
   170  				resPath := append(path, internal.Message_reservedRangeTag)
   171  				sci.newLoc(child, resPath)
   172  				for _, rr := range child.Ranges {
   173  					r.generateSourceCodeInfoForReservedRange(sci, rr, append(resPath, reservedRangeIndex))
   174  					reservedRangeIndex++
   175  				}
   176  			}
   177  		}
   178  	}
   179  }
   180  
   181  func (r *parseResult) generateSourceCodeInfoForEnum(sci *sourceCodeInfo, n *ast.EnumNode, path []int32) {
   182  	sci.newLoc(n, path)
   183  	sci.newLoc(n.Name, append(path, internal.Enum_nameTag))
   184  
   185  	var optIndex, valIndex, reservedNameIndex, reservedRangeIndex int32
   186  	for _, child := range n.Decls {
   187  		switch child := child.(type) {
   188  		case *ast.OptionNode:
   189  			r.generateSourceCodeInfoForOption(sci, child, false, &optIndex, append(path, internal.Enum_optionsTag))
   190  		case *ast.EnumValueNode:
   191  			r.generateSourceCodeInfoForEnumValue(sci, child, append(path, internal.Enum_valuesTag, valIndex))
   192  			valIndex++
   193  		case *ast.ReservedNode:
   194  			if len(child.Names) > 0 {
   195  				resPath := append(path, internal.Enum_reservedNameTag)
   196  				sci.newLoc(child, resPath)
   197  				for _, rn := range child.Names {
   198  					sci.newLoc(rn, append(resPath, reservedNameIndex))
   199  					reservedNameIndex++
   200  				}
   201  			}
   202  			if len(child.Ranges) > 0 {
   203  				resPath := append(path, internal.Enum_reservedRangeTag)
   204  				sci.newLoc(child, resPath)
   205  				for _, rr := range child.Ranges {
   206  					r.generateSourceCodeInfoForReservedRange(sci, rr, append(resPath, reservedRangeIndex))
   207  					reservedRangeIndex++
   208  				}
   209  			}
   210  		}
   211  	}
   212  }
   213  
   214  func (r *parseResult) generateSourceCodeInfoForEnumValue(sci *sourceCodeInfo, n *ast.EnumValueNode, path []int32) {
   215  	sci.newLoc(n, path)
   216  	sci.newLoc(n.Name, append(path, internal.EnumVal_nameTag))
   217  	sci.newLoc(n.Number, append(path, internal.EnumVal_numberTag))
   218  
   219  	// enum value options
   220  	if n.Options != nil {
   221  		optsPath := append(path, internal.EnumVal_optionsTag)
   222  		sci.newLoc(n.Options, optsPath)
   223  		var optIndex int32
   224  		for _, opt := range n.Options.GetElements() {
   225  			r.generateSourceCodeInfoForOption(sci, opt, true, &optIndex, optsPath)
   226  		}
   227  	}
   228  }
   229  
   230  func (r *parseResult) generateSourceCodeInfoForReservedRange(sci *sourceCodeInfo, n *ast.RangeNode, path []int32) {
   231  	sci.newLoc(n, path)
   232  	sci.newLoc(n.StartVal, append(path, internal.ReservedRange_startTag))
   233  	if n.EndVal != nil {
   234  		sci.newLoc(n.EndVal, append(path, internal.ReservedRange_endTag))
   235  	} else if n.Max != nil {
   236  		sci.newLoc(n.Max, append(path, internal.ReservedRange_endTag))
   237  	}
   238  }
   239  
   240  func (r *parseResult) generateSourceCodeInfoForExtensions(sci *sourceCodeInfo, n *ast.ExtendNode, extendIndex, msgIndex *int32, extendPath, msgPath []int32) {
   241  	sci.newLoc(n, extendPath)
   242  	for _, decl := range n.Decls {
   243  		switch decl := decl.(type) {
   244  		case *ast.FieldNode:
   245  			r.generateSourceCodeInfoForField(sci, decl, append(extendPath, *extendIndex))
   246  			*extendIndex++
   247  		case *ast.GroupNode:
   248  			fldPath := append(extendPath, *extendIndex)
   249  			r.generateSourceCodeInfoForField(sci, decl, fldPath)
   250  			*extendIndex++
   251  			r.generateSourceCodeInfoForMessage(sci, decl, fldPath, append(msgPath, *msgIndex))
   252  			*msgIndex++
   253  		}
   254  	}
   255  }
   256  
   257  func (r *parseResult) generateSourceCodeInfoForOneOf(sci *sourceCodeInfo, n *ast.OneOfNode, fieldIndex, nestedMsgIndex *int32, fieldPath, nestedMsgPath, oneOfPath []int32) {
   258  	sci.newLoc(n, oneOfPath)
   259  	sci.newLoc(n.Name, append(oneOfPath, internal.OneOf_nameTag))
   260  
   261  	var optIndex int32
   262  	for _, child := range n.Decls {
   263  		switch child := child.(type) {
   264  		case *ast.OptionNode:
   265  			r.generateSourceCodeInfoForOption(sci, child, false, &optIndex, append(oneOfPath, internal.OneOf_optionsTag))
   266  		case *ast.FieldNode:
   267  			r.generateSourceCodeInfoForField(sci, child, append(fieldPath, *fieldIndex))
   268  			*fieldIndex++
   269  		case *ast.GroupNode:
   270  			fldPath := append(fieldPath, *fieldIndex)
   271  			r.generateSourceCodeInfoForField(sci, child, fldPath)
   272  			*fieldIndex++
   273  			r.generateSourceCodeInfoForMessage(sci, child, fldPath, append(nestedMsgPath, *nestedMsgIndex))
   274  			*nestedMsgIndex++
   275  		}
   276  	}
   277  }
   278  
   279  func (r *parseResult) generateSourceCodeInfoForField(sci *sourceCodeInfo, n ast.FieldDeclNode, path []int32) {
   280  	var fieldType string
   281  	if f, ok := n.(*ast.FieldNode); ok {
   282  		fieldType = string(f.FldType.AsIdentifier())
   283  	}
   284  
   285  	if n.GetGroupKeyword() != nil {
   286  		// comments will appear on group message
   287  		sci.newLocWithoutComments(n, path)
   288  		if n.FieldExtendee() != nil {
   289  			sci.newLoc(n.FieldExtendee(), append(path, internal.Field_extendeeTag))
   290  		}
   291  		if n.FieldLabel() != nil {
   292  			// no comments here either (label is first token for group, so we want
   293  			// to leave the comments to be associated with the group message instead)
   294  			sci.newLocWithoutComments(n.FieldLabel(), append(path, internal.Field_labelTag))
   295  		}
   296  		sci.newLoc(n.FieldType(), append(path, internal.Field_typeTag))
   297  		// let the name comments be attributed to the group name
   298  		sci.newLocWithoutComments(n.FieldName(), append(path, internal.Field_nameTag))
   299  	} else {
   300  		sci.newLoc(n, path)
   301  		if n.FieldExtendee() != nil {
   302  			sci.newLoc(n.FieldExtendee(), append(path, internal.Field_extendeeTag))
   303  		}
   304  		if n.FieldLabel() != nil {
   305  			sci.newLoc(n.FieldLabel(), append(path, internal.Field_labelTag))
   306  		}
   307  		var tag int32
   308  		if _, isScalar := fieldTypes[fieldType]; isScalar {
   309  			tag = internal.Field_typeTag
   310  		} else {
   311  			// this is a message or an enum, so attribute type location
   312  			// to the type name field
   313  			tag = internal.Field_typeNameTag
   314  		}
   315  		sci.newLoc(n.FieldType(), append(path, tag))
   316  		sci.newLoc(n.FieldName(), append(path, internal.Field_nameTag))
   317  	}
   318  	sci.newLoc(n.FieldTag(), append(path, internal.Field_numberTag))
   319  
   320  	if n.GetOptions() != nil {
   321  		optsPath := append(path, internal.Field_optionsTag)
   322  		sci.newLoc(n.GetOptions(), optsPath)
   323  		var optIndex int32
   324  		for _, opt := range n.GetOptions().GetElements() {
   325  			r.generateSourceCodeInfoForOption(sci, opt, true, &optIndex, optsPath)
   326  		}
   327  	}
   328  }
   329  
   330  func (r *parseResult) generateSourceCodeInfoForExtensionRanges(sci *sourceCodeInfo, n *ast.ExtensionRangeNode, extRangeIndex *int32, path []int32) {
   331  	sci.newLoc(n, path)
   332  	for _, child := range n.Ranges {
   333  		path := append(path, *extRangeIndex)
   334  		*extRangeIndex++
   335  		sci.newLoc(child, path)
   336  		sci.newLoc(child.StartVal, append(path, internal.ExtensionRange_startTag))
   337  		if child.EndVal != nil {
   338  			sci.newLoc(child.EndVal, append(path, internal.ExtensionRange_endTag))
   339  		} else if child.Max != nil {
   340  			sci.newLoc(child.Max, append(path, internal.ExtensionRange_endTag))
   341  		}
   342  		if n.Options != nil {
   343  			optsPath := append(path, internal.ExtensionRange_optionsTag)
   344  			sci.newLoc(n.Options, optsPath)
   345  			var optIndex int32
   346  			for _, opt := range n.Options.GetElements() {
   347  				r.generateSourceCodeInfoForOption(sci, opt, true, &optIndex, optsPath)
   348  			}
   349  		}
   350  	}
   351  }
   352  
   353  func (r *parseResult) generateSourceCodeInfoForService(sci *sourceCodeInfo, n *ast.ServiceNode, path []int32) {
   354  	sci.newLoc(n, path)
   355  	sci.newLoc(n.Name, append(path, internal.Service_nameTag))
   356  	var optIndex, rpcIndex int32
   357  	for _, child := range n.Decls {
   358  		switch child := child.(type) {
   359  		case *ast.OptionNode:
   360  			r.generateSourceCodeInfoForOption(sci, child, false, &optIndex, append(path, internal.Service_optionsTag))
   361  		case *ast.RPCNode:
   362  			r.generateSourceCodeInfoForMethod(sci, child, append(path, internal.Service_methodsTag, rpcIndex))
   363  			rpcIndex++
   364  		}
   365  	}
   366  }
   367  
   368  func (r *parseResult) generateSourceCodeInfoForMethod(sci *sourceCodeInfo, n *ast.RPCNode, path []int32) {
   369  	sci.newLoc(n, path)
   370  	sci.newLoc(n.Name, append(path, internal.Method_nameTag))
   371  	if n.Input.Stream != nil {
   372  		sci.newLoc(n.Input.Stream, append(path, internal.Method_inputStreamTag))
   373  	}
   374  	sci.newLoc(n.Input.MessageType, append(path, internal.Method_inputTag))
   375  	if n.Output.Stream != nil {
   376  		sci.newLoc(n.Output.Stream, append(path, internal.Method_outputStreamTag))
   377  	}
   378  	sci.newLoc(n.Output.MessageType, append(path, internal.Method_outputTag))
   379  
   380  	optsPath := append(path, internal.Method_optionsTag)
   381  	var optIndex int32
   382  	for _, decl := range n.Decls {
   383  		if opt, ok := decl.(*ast.OptionNode); ok {
   384  			r.generateSourceCodeInfoForOption(sci, opt, false, &optIndex, optsPath)
   385  		}
   386  	}
   387  }
   388  
   389  type sourceCodeInfo struct {
   390  	locs         []*dpb.SourceCodeInfo_Location
   391  	commentsUsed map[*ast.Comment]struct{}
   392  }
   393  
   394  func (sci *sourceCodeInfo) newLocWithoutComments(n ast.Node, path []int32) {
   395  	dup := make([]int32, len(path))
   396  	copy(dup, path)
   397  	sci.locs = append(sci.locs, &dpb.SourceCodeInfo_Location{
   398  		Path: dup,
   399  		Span: makeSpan(n.Start(), n.End()),
   400  	})
   401  }
   402  
   403  func (sci *sourceCodeInfo) newLoc(n ast.Node, path []int32) {
   404  	leadingComments := n.LeadingComments()
   405  	trailingComments := n.TrailingComments()
   406  	if sci.commentUsed(leadingComments) {
   407  		leadingComments = nil
   408  	}
   409  	if sci.commentUsed(trailingComments) {
   410  		trailingComments = nil
   411  	}
   412  	detached := groupComments(leadingComments)
   413  	var trail *string
   414  	if str, ok := combineComments(trailingComments); ok {
   415  		trail = proto.String(str)
   416  	}
   417  	var lead *string
   418  	if len(leadingComments) > 0 && leadingComments[len(leadingComments)-1].End.Line >= n.Start().Line-1 {
   419  		lead = proto.String(detached[len(detached)-1])
   420  		detached = detached[:len(detached)-1]
   421  	}
   422  	dup := make([]int32, len(path))
   423  	copy(dup, path)
   424  	sci.locs = append(sci.locs, &dpb.SourceCodeInfo_Location{
   425  		LeadingDetachedComments: detached,
   426  		LeadingComments:         lead,
   427  		TrailingComments:        trail,
   428  		Path:                    dup,
   429  		Span:                    makeSpan(n.Start(), n.End()),
   430  	})
   431  }
   432  
   433  func makeSpan(start, end *SourcePos) []int32 {
   434  	if start.Line == end.Line {
   435  		return []int32{int32(start.Line) - 1, int32(start.Col) - 1, int32(end.Col) - 1}
   436  	}
   437  	return []int32{int32(start.Line) - 1, int32(start.Col) - 1, int32(end.Line) - 1, int32(end.Col) - 1}
   438  }
   439  
   440  func (sci *sourceCodeInfo) commentUsed(c []ast.Comment) bool {
   441  	if len(c) == 0 {
   442  		return false
   443  	}
   444  	if _, ok := sci.commentsUsed[&c[0]]; ok {
   445  		return true
   446  	}
   447  
   448  	sci.commentsUsed[&c[0]] = struct{}{}
   449  	return false
   450  }
   451  
   452  func groupComments(comments []ast.Comment) []string {
   453  	if len(comments) == 0 {
   454  		return nil
   455  	}
   456  
   457  	var groups []string
   458  	singleLineStyle := comments[0].Text[:2] == "//"
   459  	line := comments[0].End.Line
   460  	start := 0
   461  	for i := 1; i < len(comments); i++ {
   462  		c := comments[i]
   463  		prevSingleLine := singleLineStyle
   464  		singleLineStyle = strings.HasPrefix(comments[i].Text, "//")
   465  		if !singleLineStyle || prevSingleLine != singleLineStyle || c.Start.Line > line+1 {
   466  			// new group!
   467  			if str, ok := combineComments(comments[start:i]); ok {
   468  				groups = append(groups, str)
   469  			}
   470  			start = i
   471  		}
   472  		line = c.End.Line
   473  	}
   474  	// don't forget last group
   475  	if str, ok := combineComments(comments[start:]); ok {
   476  		groups = append(groups, str)
   477  	}
   478  	return groups
   479  }
   480  
   481  func combineComments(comments []ast.Comment) (string, bool) {
   482  	if len(comments) == 0 {
   483  		return "", false
   484  	}
   485  	var buf bytes.Buffer
   486  	for _, c := range comments {
   487  		if c.Text[:2] == "//" {
   488  			buf.WriteString(c.Text[2:])
   489  		} else {
   490  			lines := strings.Split(c.Text[2:len(c.Text)-2], "\n")
   491  			first := true
   492  			for _, l := range lines {
   493  				if first {
   494  					first = false
   495  				} else {
   496  					buf.WriteByte('\n')
   497  				}
   498  
   499  				// strip a prefix of whitespace followed by '*'
   500  				j := 0
   501  				for j < len(l) {
   502  					if l[j] != ' ' && l[j] != '\t' {
   503  						break
   504  					}
   505  					j++
   506  				}
   507  				if j == len(l) {
   508  					l = ""
   509  				} else if l[j] == '*' {
   510  					l = l[j+1:]
   511  				} else if j > 0 {
   512  					l = " " + l[j:]
   513  				}
   514  
   515  				buf.WriteString(l)
   516  			}
   517  		}
   518  	}
   519  	return buf.String(), true
   520  }
   521  
   522  func dup(p []int32) []int32 {
   523  	return append(([]int32)(nil), p...)
   524  }