github.com/danbrough/mobile@v0.0.3-beta03/internal/importers/objc/objc.go (about)

     1  // Copyright 2016 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // The objc package takes the result of an AST traversal by the
     6  // importers package and uses the clang command to dump the type
     7  // information for the referenced ObjC classes and protocols.
     8  //
     9  // It is the of go/types for ObjC types and is used by the bind
    10  // package to generate Go wrappers for ObjC API on iOS.
    11  package objc
    12  
    13  import (
    14  	"bufio"
    15  	"bytes"
    16  	"fmt"
    17  	"os/exec"
    18  	"strings"
    19  	"unicode"
    20  	"unicode/utf8"
    21  
    22  	"github.com/danbrough/mobile/internal/importers"
    23  )
    24  
    25  type parser struct {
    26  	sdkPath string
    27  	sc      *bufio.Scanner
    28  
    29  	decl   string
    30  	indent int
    31  	last   string
    32  	// Current module as parsed from the AST tree.
    33  	module string
    34  }
    35  
    36  type TypeKind int
    37  
    38  // Named represents ObjC classes and protocols.
    39  type Named struct {
    40  	Name       string
    41  	GoName     string
    42  	Module     string
    43  	Funcs      []*Func
    44  	Methods    []*Func
    45  	AllMethods []*Func
    46  	Supers     []Super
    47  	// For deduplication of function or method
    48  	// declarations.
    49  	funcMap  map[string]struct{}
    50  	Protocol bool
    51  	// Generated is true if the type is wrapper of a
    52  	// generated Go struct.
    53  	Generated bool
    54  }
    55  
    56  // Super denotes a super class or protocol.
    57  type Super struct {
    58  	Name     string
    59  	Protocol bool
    60  }
    61  
    62  // Func is a ObjC method, static functions as well as
    63  // instance methods.
    64  type Func struct {
    65  	Sig    string
    66  	GoName string
    67  	Params []*Param
    68  	Ret    *Type
    69  	Static bool
    70  	// Method whose name start with "init"
    71  	Constructor bool
    72  }
    73  
    74  type Param struct {
    75  	Name string
    76  	Type *Type
    77  }
    78  
    79  type Type struct {
    80  	Kind TypeKind
    81  	// For Interface and Protocol types.
    82  	Name string
    83  	// For 'id' types.
    84  	instanceType bool
    85  	// The declared type raw from the AST.
    86  	Decl string
    87  	// Set if the type is a pointer to its kind. For classes
    88  	// Indirect is true if the type is a double pointer, e.g.
    89  	// NSObject **.
    90  	Indirect bool
    91  }
    92  
    93  const (
    94  	Unknown TypeKind = iota
    95  	Protocol
    96  	Class
    97  	String
    98  	Data
    99  	Int
   100  	Uint
   101  	Short
   102  	Ushort
   103  	Bool
   104  	Char
   105  	Uchar
   106  	Float
   107  	Double
   108  )
   109  
   110  // Import returns  descriptors for a list of references to
   111  // ObjC protocols and classes.
   112  //
   113  // The type information is parsed from the output of clang -cc1
   114  // -ast-dump.
   115  func Import(refs *importers.References) ([]*Named, error) {
   116  	var modules []string
   117  	modMap := make(map[string]struct{})
   118  	typeNames := make(map[string][]string)
   119  	typeSet := make(map[string]struct{})
   120  	genMods := make(map[string]struct{})
   121  	for _, emb := range refs.Embedders {
   122  		genMods[initialUpper(emb.Pkg)] = struct{}{}
   123  	}
   124  	for _, ref := range refs.Refs {
   125  		var module, name string
   126  		if idx := strings.Index(ref.Pkg, "/"); idx != -1 {
   127  			// ref is a static method reference.
   128  			module = ref.Pkg[:idx]
   129  			name = ref.Pkg[idx+1:]
   130  		} else {
   131  			// ref is a type name.
   132  			module = ref.Pkg
   133  			name = ref.Name
   134  		}
   135  		if _, exists := typeSet[name]; !exists {
   136  			typeNames[module] = append(typeNames[module], name)
   137  			typeSet[name] = struct{}{}
   138  		}
   139  		if _, exists := modMap[module]; !exists {
   140  			// Include the module only if it is generated.
   141  			if _, exists := genMods[module]; !exists {
   142  				modMap[module] = struct{}{}
   143  				modules = append(modules, module)
   144  			}
   145  		}
   146  	}
   147  	sdkPathOut, err := exec.Command("xcrun", "--sdk", "iphonesimulator", "--show-sdk-path").CombinedOutput()
   148  	if err != nil {
   149  		return nil, err
   150  	}
   151  	sdkPath := strings.TrimSpace(string(sdkPathOut))
   152  	var allTypes []*Named
   153  	typeMap := make(map[string]*Named)
   154  	for _, module := range modules {
   155  		types, err := importModule(string(sdkPath), module, typeNames[module], typeMap)
   156  		if err != nil {
   157  			return nil, fmt.Errorf("%s: %v", module, err)
   158  		}
   159  		allTypes = append(allTypes, types...)
   160  	}
   161  	// Embedders refer to every exported Go struct that will have its class
   162  	// generated. Allow Go code to reverse bind to those classes by synthesizing
   163  	// their descriptors.
   164  	for _, emb := range refs.Embedders {
   165  		module := initialUpper(emb.Pkg)
   166  		named := &Named{
   167  			Name:      module + emb.Name,
   168  			GoName:    emb.Name,
   169  			Module:    module,
   170  			Generated: true,
   171  		}
   172  		for _, ref := range emb.Refs {
   173  			t, exists := typeMap[ref.Name]
   174  			if !exists {
   175  				return nil, fmt.Errorf("type not found: %q", ref.Name)
   176  			}
   177  			named.Supers = append(named.Supers, Super{
   178  				Name:     t.Name,
   179  				Protocol: t.Protocol,
   180  			})
   181  		}
   182  		typeMap[emb.Name] = named
   183  		allTypes = append(allTypes, named)
   184  	}
   185  	initTypes(allTypes, refs, typeMap)
   186  	// Include implicit types that are used in parameter or return values.
   187  	newTypes := allTypes
   188  	for len(newTypes) > 0 {
   189  		var impTypes []*Named
   190  		for _, t := range newTypes {
   191  			for _, funcs := range [][]*Func{t.Funcs, t.AllMethods} {
   192  				for _, f := range funcs {
   193  					types := implicitFuncTypes(f)
   194  					for _, name := range types {
   195  						if _, exists := typeSet[name]; exists {
   196  							continue
   197  						}
   198  						typeSet[name] = struct{}{}
   199  						t, exists := typeMap[name]
   200  						if !exists {
   201  							return nil, fmt.Errorf("implicit type %q not found", name)
   202  						}
   203  						impTypes = append(impTypes, t)
   204  					}
   205  				}
   206  			}
   207  		}
   208  		initTypes(impTypes, refs, typeMap)
   209  		allTypes = append(allTypes, impTypes...)
   210  		newTypes = impTypes
   211  	}
   212  	return allTypes, nil
   213  }
   214  
   215  func implicitFuncTypes(f *Func) []string {
   216  	var types []string
   217  	if rt := f.Ret; rt != nil && !rt.instanceType && (rt.Kind == Class || rt.Kind == Protocol) {
   218  		types = append(types, rt.Name)
   219  	}
   220  	for _, p := range f.Params {
   221  		if t := p.Type; !t.instanceType && (t.Kind == Class || t.Kind == Protocol) {
   222  			types = append(types, t.Name)
   223  		}
   224  	}
   225  	return types
   226  }
   227  
   228  func initTypes(types []*Named, refs *importers.References, typeMap map[string]*Named) {
   229  	for _, t := range types {
   230  		fillAllMethods(t, typeMap)
   231  	}
   232  	// Move constructors to functions. They are represented in Go
   233  	// as functions.
   234  	for _, t := range types {
   235  		var methods []*Func
   236  		for _, f := range t.AllMethods {
   237  			if f.Constructor {
   238  				f.Static = true
   239  				t.Funcs = append(t.Funcs, f)
   240  			} else {
   241  				methods = append(methods, f)
   242  			}
   243  		}
   244  		t.AllMethods = methods
   245  	}
   246  	for _, t := range types {
   247  		mangleMethodNames(t.AllMethods)
   248  		mangleMethodNames(t.Funcs)
   249  	}
   250  	filterReferences(types, refs, typeMap)
   251  	for _, t := range types {
   252  		resolveInstanceTypes(t, t.Funcs)
   253  		resolveInstanceTypes(t, t.AllMethods)
   254  	}
   255  }
   256  
   257  func filterReferences(types []*Named, refs *importers.References, typeMap map[string]*Named) {
   258  	refFuncs := make(map[[2]string]struct{})
   259  	for _, ref := range refs.Refs {
   260  		if sep := strings.Index(ref.Pkg, "/"); sep != -1 {
   261  			pkgName := ref.Pkg[sep+1:]
   262  			n := typeMap[pkgName]
   263  			if n == nil {
   264  				continue
   265  			}
   266  			refFuncs[[...]string{pkgName, ref.Name}] = struct{}{}
   267  		}
   268  	}
   269  	for _, t := range types {
   270  		var filtered []*Func
   271  		for _, f := range t.Funcs {
   272  			if _, exists := refFuncs[[...]string{t.GoName, f.GoName}]; exists {
   273  				filtered = append(filtered, f)
   274  			}
   275  		}
   276  		t.Funcs = filtered
   277  		filtered = nil
   278  		for _, m := range t.Methods {
   279  			if _, exists := refs.Names[m.GoName]; exists {
   280  				filtered = append(filtered, m)
   281  			}
   282  		}
   283  		t.Methods = filtered
   284  		filtered = nil
   285  		for _, m := range t.AllMethods {
   286  			if _, exists := refs.Names[m.GoName]; exists {
   287  				filtered = append(filtered, m)
   288  			}
   289  		}
   290  		t.AllMethods = filtered
   291  	}
   292  }
   293  
   294  // mangleMethodsNames assigns unique Go names to ObjC methods. If a method name is unique
   295  // within the same method list, its name is used with its first letter in upper case.
   296  // Multiple methods with the same name have their full signature appended, with : removed.
   297  func mangleMethodNames(allFuncs []*Func) {
   298  	goName := func(n string, constructor bool) string {
   299  		if constructor {
   300  			n = "new" + n[len("init"):]
   301  		}
   302  		return initialUpper(n)
   303  	}
   304  	overloads := make(map[string][]*Func)
   305  	for i, f := range allFuncs {
   306  		// Copy function so each class can have its own
   307  		// name mangling.
   308  		f := *f
   309  		allFuncs[i] = &f
   310  		f.GoName = goName(f.Sig, f.Constructor)
   311  		if colon := strings.Index(f.GoName, ":"); colon != -1 {
   312  			f.GoName = f.GoName[:colon]
   313  		}
   314  		overloads[f.GoName] = append(overloads[f.GoName], &f)
   315  	}
   316  	fallbacks := make(map[string][]*Func)
   317  	for _, funcs := range overloads {
   318  		if len(funcs) == 1 {
   319  			continue
   320  		}
   321  		for _, f := range funcs {
   322  			sig := f.Sig
   323  			if strings.HasSuffix(sig, ":") {
   324  				sig = sig[:len(sig)-1]
   325  			}
   326  			sigElems := strings.Split(f.Sig, ":")
   327  			for i := 0; i < len(sigElems); i++ {
   328  				sigElems[i] = initialUpper(sigElems[i])
   329  			}
   330  			name := strings.Join(sigElems, "")
   331  			f.GoName = goName(name, f.Constructor)
   332  			fallbacks[f.GoName] = append(fallbacks[f.GoName], f)
   333  		}
   334  	}
   335  	for _, funcs := range fallbacks {
   336  		if len(funcs) == 1 {
   337  			continue
   338  		}
   339  		for _, f := range funcs {
   340  			name := strings.Replace(f.Sig, ":", "_", -1)
   341  			f.GoName = goName(name, f.Constructor)
   342  		}
   343  	}
   344  }
   345  
   346  func resolveInstanceType(n *Named, t *Type) *Type {
   347  	if !t.instanceType || t.Kind != Protocol {
   348  		return t
   349  	}
   350  	// Copy and update the type name for instancetype types
   351  	ct := *t
   352  	ct.instanceType = false
   353  	ct.Decl = n.Name + " *"
   354  	if n.Name == "NSString" {
   355  		ct.Kind = String
   356  		ct.Name = ""
   357  	} else {
   358  		ct.Kind = Class
   359  		ct.Name = n.Name
   360  	}
   361  	return &ct
   362  }
   363  
   364  func resolveInstanceTypes(n *Named, funcs []*Func) {
   365  	for _, f := range funcs {
   366  		for _, p := range f.Params {
   367  			p.Type = resolveInstanceType(n, p.Type)
   368  		}
   369  		if f.Ret != nil {
   370  			f.Ret = resolveInstanceType(n, f.Ret)
   371  		}
   372  	}
   373  }
   374  
   375  func fillAllMethods(n *Named, typeMap map[string]*Named) {
   376  	if len(n.AllMethods) > 0 {
   377  		return
   378  	}
   379  	if len(n.Supers) == 0 {
   380  		n.AllMethods = n.Methods
   381  		return
   382  	}
   383  	for _, sup := range n.Supers {
   384  		super := lookup(sup.Name, sup.Protocol, typeMap)
   385  		fillAllMethods(super, typeMap)
   386  	}
   387  	methods := make(map[string]struct{})
   388  	for _, sup := range n.Supers {
   389  		super := lookup(sup.Name, sup.Protocol, typeMap)
   390  		for _, f := range super.AllMethods {
   391  			if _, exists := methods[f.Sig]; !exists {
   392  				methods[f.Sig] = struct{}{}
   393  				n.AllMethods = append(n.AllMethods, f)
   394  			}
   395  		}
   396  	}
   397  	for _, f := range n.Methods {
   398  		if _, exists := methods[f.Sig]; !exists {
   399  			n.AllMethods = append(n.AllMethods, f)
   400  		}
   401  	}
   402  }
   403  
   404  const (
   405  	frameworksPath = "/System/Library/Frameworks/"
   406  )
   407  
   408  // importModule parses ObjC type information with clang -cc1 -ast-dump.
   409  //
   410  // TODO: Use module.map files to precisely model the @import Module.Identifier
   411  // directive. For now, importModules assumes the single umbrella header
   412  // file Module.framework/Headers/Module.h contains every declaration.
   413  func importModule(sdkPath, module string, identifiers []string, typeMap map[string]*Named) ([]*Named, error) {
   414  	hFile := fmt.Sprintf(sdkPath+frameworksPath+"%s.framework/Headers/%[1]s.h", module)
   415  	clang := exec.Command("xcrun", "--sdk", "iphonesimulator", "clang", "-cc1", "-triple", "x86_64-apple-ios8.0.0-simulator", "-isysroot", sdkPath, "-ast-dump", "-fblocks", "-fobjc-arc", "-x", "objective-c", hFile)
   416  	out, err := clang.CombinedOutput()
   417  	if err != nil {
   418  		return nil, fmt.Errorf("clang failed to parse module: %v: %s", err, out)
   419  	}
   420  	p := &parser{
   421  		sdkPath: sdkPath,
   422  		sc:      bufio.NewScanner(bytes.NewBuffer(out)),
   423  	}
   424  	if err := p.parseModule(module, typeMap); err != nil {
   425  		return nil, err
   426  	}
   427  	var types []*Named
   428  	for _, ident := range identifiers {
   429  		named, exists := typeMap[ident]
   430  		if !exists {
   431  			return nil, fmt.Errorf("no such type: %s", ident)
   432  		}
   433  		types = append(types, named)
   434  	}
   435  	return types, nil
   436  }
   437  
   438  func (p *parser) scanLine() bool {
   439  	for {
   440  		l := p.last
   441  		if l == "" {
   442  			if !p.sc.Scan() {
   443  				return false
   444  			}
   445  			l = p.sc.Text()
   446  		} else {
   447  			p.last = ""
   448  		}
   449  		indent := (strings.Index(l, "-") + 1) / 2
   450  		switch {
   451  		case indent > p.indent:
   452  			// Skip
   453  		case indent < p.indent:
   454  			p.indent--
   455  			p.last = l
   456  			return false
   457  		case indent == p.indent:
   458  			p.decl = l[p.indent*2:]
   459  			return true
   460  		}
   461  	}
   462  }
   463  
   464  func (p *parser) parseModule(module string, typeMap map[string]*Named) (err error) {
   465  	defer func() {
   466  		if rerr := recover(); rerr != nil {
   467  			err = rerr.(error)
   468  		}
   469  	}()
   470  	if !p.scanLine() {
   471  		return nil
   472  	}
   473  	// A header file AST starts with
   474  	//
   475  	// TranslationUnitDecl 0x103833ad0 <<invalid sloc>> <invalid sloc>
   476  	if w := p.scanWord(); w != "TranslationUnitDecl" {
   477  		return fmt.Errorf("unexpected AST root: %q", w)
   478  	}
   479  	p.indent++
   480  	for {
   481  		if !p.scanLine() {
   482  			break
   483  		}
   484  		switch w := p.scanWord(); w {
   485  		case "ObjCCategoryDecl":
   486  			// ObjCCategoryDecl 0x103d9bdb8 <line:48:1, line:63:2> line:48:12 NSDateCreation
   487  			// |-ObjCInterface 0x103d9a788 'NSDate'
   488  			// Skip the node address, the source code range, position.
   489  			p.scanWord()
   490  			p.parseLocation()
   491  			catName := p.scanWord()
   492  			p.indent++
   493  			if !p.scanLine() {
   494  				return fmt.Errorf("no interface for category %s", catName)
   495  			}
   496  			if w := p.scanWord(); w != "ObjCInterface" {
   497  				return fmt.Errorf("unexpected declaaration %s for category %s", w, catName)
   498  			}
   499  			p.scanWord()
   500  			clsName := p.scanWord()
   501  			clsName = clsName[1 : len(clsName)-1]
   502  			named := lookup(clsName, false, typeMap)
   503  			if named == nil {
   504  				return fmt.Errorf("category %s references unknown class %s", catName, clsName)
   505  			}
   506  			p.parseInterface(named)
   507  		case "ObjCInterfaceDecl", "ObjCProtocolDecl":
   508  			// ObjCProtocolDecl 0x104116450 <line:15:1, line:47:2> line:15:11 NSObject
   509  			// or
   510  			// ObjCInterfaceDecl 0x1041ca480 <line:17:29, line:64:2> line:17:40 UIResponder
   511  
   512  			prot := w == "ObjCProtocolDecl"
   513  
   514  			// Skip the node address, the source code range, position.
   515  			p.scanWord()
   516  			if strings.HasPrefix(p.decl, "prev ") {
   517  				p.scanWord()
   518  				p.scanWord()
   519  			}
   520  			p.parseLocation()
   521  			if strings.HasPrefix(p.decl, "implicit ") {
   522  				p.scanWord()
   523  			}
   524  			name := p.decl
   525  			named := p.lookupOrCreate(name, prot, typeMap)
   526  			p.indent++
   527  			p.parseInterface(named)
   528  		default:
   529  		}
   530  	}
   531  	return nil
   532  }
   533  
   534  func lookup(name string, prot bool, typeMap map[string]*Named) *Named {
   535  	var mangled string
   536  	if prot {
   537  		mangled = name + "P"
   538  	} else {
   539  		mangled = name + "C"
   540  	}
   541  	if n := typeMap[mangled]; n != nil {
   542  		return n
   543  	}
   544  	return typeMap[name]
   545  }
   546  
   547  // lookupOrCreate looks up the type name in the type map. If it doesn't exist, it creates
   548  // and returns a new type. If it does exist, it returns the existing type. If there are both
   549  // a class and a protocol with the same name, their type names are mangled by prefixing
   550  // 'C' or 'P' and then re-inserted into the type map.
   551  func (p *parser) lookupOrCreate(name string, prot bool, typeMap map[string]*Named) *Named {
   552  	mangled := name + "C"
   553  	otherMangled := name + "P"
   554  	if prot {
   555  		mangled, otherMangled = otherMangled, mangled
   556  	}
   557  	named, exists := typeMap[mangled]
   558  	if exists {
   559  		return named
   560  	}
   561  	named, exists = typeMap[name]
   562  	if exists {
   563  		if named.Protocol == prot {
   564  			return named
   565  		}
   566  		// Both a class and a protocol exists with the same name.
   567  		delete(typeMap, name)
   568  		named.GoName = otherMangled
   569  		typeMap[otherMangled] = named
   570  		named = &Named{
   571  			GoName: mangled,
   572  		}
   573  	} else {
   574  		named = &Named{
   575  			GoName: name,
   576  		}
   577  	}
   578  	named.Name = name
   579  	named.Protocol = prot
   580  	named.funcMap = make(map[string]struct{})
   581  	named.Module = p.module
   582  	typeMap[named.GoName] = named
   583  	return named
   584  }
   585  
   586  func (p *parser) parseInterface(n *Named) {
   587  	for {
   588  		more := p.scanLine()
   589  		if !more {
   590  			break
   591  		}
   592  		switch w := p.scanWord(); w {
   593  		case "super":
   594  			if w := p.scanWord(); w != "ObjCInterface" {
   595  				panic(fmt.Errorf("unknown super type: %s", w))
   596  			}
   597  			// Skip node address.
   598  			p.scanWord()
   599  			super := p.scanWord()
   600  			// Remove single quotes
   601  			super = super[1 : len(super)-1]
   602  			n.Supers = append(n.Supers, Super{super, false})
   603  		case "ObjCProtocol":
   604  			p.scanWord()
   605  			super := p.scanWord()
   606  			super = super[1 : len(super)-1]
   607  			n.Supers = append(n.Supers, Super{super, true})
   608  		case "ObjCMethodDecl":
   609  			f := p.parseMethod()
   610  			if f == nil {
   611  				continue
   612  			}
   613  			var key string
   614  			if f.Static {
   615  				key = "+" + f.Sig
   616  			} else {
   617  				key = "-" + f.Sig
   618  			}
   619  			if _, exists := n.funcMap[key]; !exists {
   620  				n.funcMap[key] = struct{}{}
   621  				if f.Static {
   622  					n.Funcs = append(n.Funcs, f)
   623  				} else {
   624  					n.Methods = append(n.Methods, f)
   625  				}
   626  			}
   627  		}
   628  	}
   629  }
   630  
   631  func (p *parser) parseMethod() *Func {
   632  	// ObjCMethodDecl 0x103bdfb80 <line:17:1, col:27> col:1 - isEqual: 'BOOL':'_Bool'
   633  
   634  	// Skip the address, range, position.
   635  	p.scanWord()
   636  	p.parseLocation()
   637  	if strings.HasPrefix(p.decl, "implicit") {
   638  		p.scanWord()
   639  	}
   640  	f := new(Func)
   641  	switch w := p.scanWord(); w {
   642  	case "+":
   643  		f.Static = true
   644  	case "-":
   645  		f.Static = false
   646  	default:
   647  		panic(fmt.Errorf("unknown method type for %q", w))
   648  	}
   649  	f.Sig = p.scanWord()
   650  	if f.Sig == "dealloc" {
   651  		// ARC forbids dealloc
   652  		return nil
   653  	}
   654  	if strings.HasPrefix(f.Sig, "init") {
   655  		f.Constructor = true
   656  	}
   657  	f.Ret = p.parseType()
   658  	p.indent++
   659  	for {
   660  		more := p.scanLine()
   661  		if !more {
   662  			break
   663  		}
   664  		switch p.scanWord() {
   665  		case "UnavailableAttr":
   666  			p.indent--
   667  			return nil
   668  		case "ParmVarDecl":
   669  			f.Params = append(f.Params, p.parseParameter())
   670  		}
   671  	}
   672  	return f
   673  }
   674  
   675  func (p *parser) parseParameter() *Param {
   676  	// ParmVarDecl 0x1041caca8 <col:70, col:80> col:80 event 'UIEvent * _Nullable':'UIEvent *'
   677  
   678  	// Skip address, source range, position.
   679  	p.scanWord()
   680  	p.parseLocation()
   681  	return &Param{Name: p.scanWord(), Type: p.parseType()}
   682  }
   683  
   684  func (p *parser) parseType() *Type {
   685  	// NSUInteger':'unsigned long'
   686  	s := strings.SplitN(p.decl, ":", 2)
   687  	decl := s[0]
   688  	var canon string
   689  	if len(s) == 2 {
   690  		canon = s[1]
   691  	} else {
   692  		canon = decl
   693  	}
   694  	// unquote the type
   695  	canon = canon[1 : len(canon)-1]
   696  	if canon == "void" {
   697  		return nil
   698  	}
   699  	decl = decl[1 : len(decl)-1]
   700  	instancetype := strings.HasPrefix(decl, "instancetype")
   701  	// Strip modifiers
   702  	mods := []string{"__strong", "__unsafe_unretained", "const", "__strong", "_Nonnull", "_Nullable", "__autoreleasing"}
   703  	for _, mod := range mods {
   704  		if idx := strings.Index(canon, mod); idx != -1 {
   705  			canon = canon[:idx] + canon[idx+len(mod):]
   706  		}
   707  		if idx := strings.Index(decl, mod); idx != -1 {
   708  			decl = decl[:idx] + decl[idx+len(mod):]
   709  		}
   710  	}
   711  	canon = strings.TrimSpace(canon)
   712  	decl = strings.TrimSpace(decl)
   713  	t := &Type{
   714  		Decl:         decl,
   715  		instanceType: instancetype,
   716  	}
   717  	switch canon {
   718  	case "int", "long", "long long":
   719  		t.Kind = Int
   720  	case "unsigned int", "unsigned long", "unsigned long long":
   721  		t.Kind = Uint
   722  	case "short":
   723  		t.Kind = Short
   724  	case "unsigned short":
   725  		t.Kind = Ushort
   726  	case "char":
   727  		t.Kind = Char
   728  	case "unsigned char":
   729  		t.Kind = Uchar
   730  	case "float":
   731  		t.Kind = Float
   732  	case "double":
   733  		t.Kind = Double
   734  	case "_Bool":
   735  		t.Kind = Bool
   736  	case "NSString *":
   737  		t.Kind = String
   738  	case "NSData *":
   739  		t.Kind = Data
   740  	default:
   741  		switch {
   742  		case strings.HasPrefix(canon, "enum"):
   743  			t.Kind = Int
   744  		case strings.HasPrefix(canon, "id"):
   745  			_, gen := p.splitGeneric(canon)
   746  			t.Kind = Protocol
   747  			t.Name = gen
   748  		default:
   749  			if ind := strings.Count(canon, "*"); 1 <= ind && ind <= 2 {
   750  				space := strings.Index(canon, " ")
   751  				name := canon[:space]
   752  				name, _ = p.splitGeneric(name)
   753  				t.Kind = Class
   754  				t.Name = name
   755  				t.Indirect = ind > 1
   756  			}
   757  		}
   758  	}
   759  	return t
   760  }
   761  
   762  func (p *parser) splitGeneric(decl string) (string, string) {
   763  	// NSArray<KeyType>
   764  	if br := strings.Index(decl, "<"); br != -1 {
   765  		return decl[:br], decl[br+1 : len(decl)-1]
   766  	} else {
   767  		return decl, ""
   768  	}
   769  }
   770  
   771  func (p *parser) parseSrcPos() {
   772  	const invPref = "<invalid sloc>"
   773  	if strings.HasPrefix(p.decl, invPref) {
   774  		p.decl = p.decl[len(invPref):]
   775  		return
   776  	}
   777  	var loc string
   778  	const scrPref = "<scratch space>"
   779  	if strings.HasPrefix(p.decl, scrPref) {
   780  		// <scratch space>:130:1
   781  		p.decl = p.decl[len(scrPref):]
   782  		loc = "line" + p.scanWord()
   783  	} else {
   784  		// line:17:2, col:18 or, a file location:
   785  		// /.../UIKit.framework/Headers/UISelectionFeedbackGenerator.h:16:1
   786  		loc = p.scanWord()
   787  	}
   788  	locs := strings.SplitN(loc, ":", 2)
   789  	if len(locs) != 2 && len(locs) != 3 {
   790  		panic(fmt.Errorf("invalid source position: %q", loc))
   791  	}
   792  	switch loc := locs[0]; loc {
   793  	case "line", "col":
   794  	default:
   795  		if !strings.HasPrefix(loc, p.sdkPath) {
   796  			panic(fmt.Errorf("invalid source position: %q", loc))
   797  		}
   798  		loc = loc[len(p.sdkPath):]
   799  		switch {
   800  		case strings.HasPrefix(loc, "/usr/include/objc/"):
   801  			p.module = "Foundation"
   802  		case strings.HasPrefix(loc, frameworksPath):
   803  			loc = loc[len(frameworksPath):]
   804  			i := strings.Index(loc, ".framework")
   805  			if i == -1 {
   806  				panic(fmt.Errorf("invalid source position: %q", loc))
   807  			}
   808  			p.module = loc[:i]
   809  			// Some types are declared in CoreFoundation.framework
   810  			// even though they belong in Foundation in Objective-C.
   811  			if p.module == "CoreFoundation" {
   812  				p.module = "Foundation"
   813  			}
   814  		default:
   815  		}
   816  	}
   817  }
   818  
   819  func (p *parser) parseLocation() {
   820  	// Source ranges are on the form: <line:17:29, line:64:2>.
   821  	if !strings.HasPrefix(p.decl, "<") {
   822  		panic(fmt.Errorf("1no source range first in %s", p.decl))
   823  	}
   824  	p.decl = p.decl[1:]
   825  	p.parseSrcPos()
   826  	if strings.HasPrefix(p.decl, ", ") {
   827  		p.decl = p.decl[2:]
   828  		p.parseSrcPos()
   829  	}
   830  	if !strings.HasPrefix(p.decl, "> ") {
   831  		panic(fmt.Errorf("no source range first in %s", p.decl))
   832  	}
   833  	p.decl = p.decl[2:]
   834  	p.parseSrcPos()
   835  }
   836  
   837  func (p *parser) scanWord() string {
   838  	i := 0
   839  loop:
   840  	for ; i < len(p.decl); i++ {
   841  		switch p.decl[i] {
   842  		case ' ', '>', ',':
   843  			break loop
   844  		}
   845  	}
   846  	w := p.decl[:i]
   847  	p.decl = p.decl[i:]
   848  	for len(p.decl) > 0 && p.decl[0] == ' ' {
   849  		p.decl = p.decl[1:]
   850  	}
   851  	return w
   852  }
   853  
   854  func initialUpper(s string) string {
   855  	if s == "" {
   856  		return ""
   857  	}
   858  	r, n := utf8.DecodeRuneInString(s)
   859  	return string(unicode.ToUpper(r)) + s[n:]
   860  }
   861  
   862  func (t *Named) ObjcType() string {
   863  	if t.Protocol {
   864  		return fmt.Sprintf("id<%s> _Nullable", t.Name)
   865  	} else {
   866  		return t.Name + " * _Nullable"
   867  	}
   868  }