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 }