[
  {
    "path": ".gitignore",
    "content": "# Compiled Object files, Static and Dynamic libs (Shared Objects)\n*.o\n*.a\n*.so\n\n# Folders\n_obj\n_test\n\n# Architecture specific extensions/prefixes\n*.[568vq]\n[568vq].out\n\n*.cgo1.go\n*.cgo2.c\n_cgo_defun.c\n_cgo_gotypes.go\n_cgo_export.*\n\n_testmain.go\n\n*.exe\n*.test\n*.prof\n\n*.dot\n*.pdf\n*_cfsms\n\ndingo-hunter\n.*~\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"third_party/gmc-synthesis\"]\n\tbranch = dingo\n\tpath = third_party/gmc-synthesis\n\turl = https://bitbucket.org/nickng/gmc-synthesis\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: go\nscript:\n    - go test -v ./...\naddons:\n    ssh_known_hosts:\n        - bitbucket.org\nnotifications:\n    slack:\n        secure: tVChzqfZMyTmdTiiVf74gNVYeG6N6ceOzU5v4fKMQhGHyXQK74SSa3+9La1fQpcO7VGduSgtvGtFN2FOY4JfqKLY7v5y93RFOBdgGOVrys5WWY2vXHKS9pHbwjIHOTmdaWvkj9i3jR0g2FiCKt1sq2LuE9Qw9qOYFN4PiZzm8NP0CM7GKhFJ1emJ8nrQMJRwA6F7LvCizaN4p0XMDCNe31WpHecAaZVivtR4JA/1r4ufRI7yD2a6B3sk8pePpCBoKm3PKDEiuSLf1n4GbBmCI4iZbOZNZ+4tIFdLJnJQc520YIHJ/DTJZo6MqDeEotod0yIQ60jIWNwrxqfYXcZ1GcLPwAe5j5GKKWnIs5JH/tPB3Le8r5RwzJ+SXPMv8XTQQ/q5PmrhRCxOVLXS/Au+1KbooJrEyYG6etiP97ucXjmcKLL4f+JKwIhBHUNNwuKTO30s0dHJO66LINd/ncO89kUyYKV6ijm8S/4BEMvZaKoJJhjnNbjUXfX6jrG20DEDNl2TvAgeZm6/LeMQ/JObdYV35+IXXyB9Ns/obs6f4+3n/XM/xQA/apOhbRPX3RUQNIcSMcyH1Mwuud3ehsMXQ298bl2t/w0x2swL7UzcNj6SFppmb1VtKJYStPtKj9wbjy8E/y7qlxYMZZSsGXeOHlUu/rtnAD6zpAKi2VJdPRY=\n"
  },
  {
    "path": "LICENSE",
    "content": "                              Apache License\n                        Version 2.0, January 2004\n                     http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n   \"License\" shall mean the terms and conditions for use, reproduction,\n   and distribution as defined by Sections 1 through 9 of this document.\n\n   \"Licensor\" shall mean the copyright owner or entity authorized by\n   the copyright owner that is granting the License.\n\n   \"Legal Entity\" shall mean the union of the acting entity and all\n   other entities that control, are controlled by, or are under common\n   control with that entity. For the purposes of this definition,\n   \"control\" means (i) the power, direct or indirect, to cause the\n   direction or management of such entity, whether by contract or\n   otherwise, or (ii) ownership of fifty percent (50%) or more of the\n   outstanding shares, or (iii) beneficial ownership of such entity.\n\n   \"You\" (or \"Your\") shall mean an individual or Legal Entity\n   exercising permissions granted by this License.\n\n   \"Source\" form shall mean the preferred form for making modifications,\n   including but not limited to software source code, documentation\n   source, and configuration files.\n\n   \"Object\" form shall mean any form resulting from mechanical\n   transformation or translation of a Source form, including but\n   not limited to compiled object code, generated documentation,\n   and conversions to other media types.\n\n   \"Work\" shall mean the work of authorship, whether in Source or\n   Object form, made available under the License, as indicated by a\n   copyright notice that is included in or attached to the work\n   (an example is provided in the Appendix below).\n\n   \"Derivative Works\" shall mean any work, whether in Source or Object\n   form, that is based on (or derived from) the Work and for which the\n   editorial revisions, annotations, elaborations, or other modifications\n   represent, as a whole, an original work of authorship. For the purposes\n   of this License, Derivative Works shall not include works that remain\n   separable from, or merely link (or bind by name) to the interfaces of,\n   the Work and Derivative Works thereof.\n\n   \"Contribution\" shall mean any work of authorship, including\n   the original version of the Work and any modifications or additions\n   to that Work or Derivative Works thereof, that is intentionally\n   submitted to Licensor for inclusion in the Work by the copyright owner\n   or by an individual or Legal Entity authorized to submit on behalf of\n   the copyright owner. For the purposes of this definition, \"submitted\"\n   means any form of electronic, verbal, or written communication sent\n   to the Licensor or its representatives, including but not limited to\n   communication on electronic mailing lists, source code control systems,\n   and issue tracking systems that are managed by, or on behalf of, the\n   Licensor for the purpose of discussing and improving the Work, but\n   excluding communication that is conspicuously marked or otherwise\n   designated in writing by the copyright owner as \"Not a Contribution.\"\n\n   \"Contributor\" shall mean Licensor and any individual or Legal Entity\n   on behalf of whom a Contribution has been received by Licensor and\n   subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   copyright license to reproduce, prepare Derivative Works of,\n   publicly display, publicly perform, sublicense, and distribute the\n   Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   (except as stated in this section) patent license to make, have made,\n   use, offer to sell, sell, import, and otherwise transfer the Work,\n   where such license applies only to those patent claims licensable\n   by such Contributor that are necessarily infringed by their\n   Contribution(s) alone or by combination of their Contribution(s)\n   with the Work to which such Contribution(s) was submitted. If You\n   institute patent litigation against any entity (including a\n   cross-claim or counterclaim in a lawsuit) alleging that the Work\n   or a Contribution incorporated within the Work constitutes direct\n   or contributory patent infringement, then any patent licenses\n   granted to You under this License for that Work shall terminate\n   as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the\n   Work or Derivative Works thereof in any medium, with or without\n   modifications, and in Source or Object form, provided that You\n   meet the following conditions:\n\n   (a) You must give any other recipients of the Work or\n       Derivative Works a copy of this License; and\n\n   (b) You must cause any modified files to carry prominent notices\n       stating that You changed the files; and\n\n   (c) You must retain, in the Source form of any Derivative Works\n       that You distribute, all copyright, patent, trademark, and\n       attribution notices from the Source form of the Work,\n       excluding those notices that do not pertain to any part of\n       the Derivative Works; and\n\n   (d) If the Work includes a \"NOTICE\" text file as part of its\n       distribution, then any Derivative Works that You distribute must\n       include a readable copy of the attribution notices contained\n       within such NOTICE file, excluding those notices that do not\n       pertain to any part of the Derivative Works, in at least one\n       of the following places: within a NOTICE text file distributed\n       as part of the Derivative Works; within the Source form or\n       documentation, if provided along with the Derivative Works; or,\n       within a display generated by the Derivative Works, if and\n       wherever such third-party notices normally appear. The contents\n       of the NOTICE file are for informational purposes only and\n       do not modify the License. You may add Your own attribution\n       notices within Derivative Works that You distribute, alongside\n       or as an addendum to the NOTICE text from the Work, provided\n       that such additional attribution notices cannot be construed\n       as modifying the License.\n\n   You may add Your own copyright statement to Your modifications and\n   may provide additional or different license terms and conditions\n   for use, reproduction, or distribution of Your modifications, or\n   for any such Derivative Works as a whole, provided Your use,\n   reproduction, and distribution of the Work otherwise complies with\n   the conditions stated in this License.\n\n5. Submission of Contributions. Unless You explicitly state otherwise,\n   any Contribution intentionally submitted for inclusion in the Work\n   by You to the Licensor shall be under the terms and conditions of\n   this License, without any additional terms or conditions.\n   Notwithstanding the above, nothing herein shall supersede or modify\n   the terms of any separate license agreement you may have executed\n   with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade\n   names, trademarks, service marks, or product names of the Licensor,\n   except as required for reasonable and customary use in describing the\n   origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or\n   agreed to in writing, Licensor provides the Work (and each\n   Contributor provides its Contributions) on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n   implied, including, without limitation, any warranties or conditions\n   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n   PARTICULAR PURPOSE. You are solely responsible for determining the\n   appropriateness of using or redistributing the Work and assume any\n   risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory,\n   whether in tort (including negligence), contract, or otherwise,\n   unless required by applicable law (such as deliberate and grossly\n   negligent acts) or agreed to in writing, shall any Contributor be\n   liable to You for damages, including any direct, indirect, special,\n   incidental, or consequential damages of any character arising as a\n   result of this License or out of the use or inability to use the\n   Work (including but not limited to damages for loss of goodwill,\n   work stoppage, computer failure or malfunction, or any and all\n   other commercial damages or losses), even if such Contributor\n   has been advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability. While redistributing\n   the Work or Derivative Works thereof, You may choose to offer,\n   and charge a fee for, acceptance of support, warranty, indemnity,\n   or other liability obligations and/or rights consistent with this\n   License. However, in accepting such obligations, You may act only\n   on Your own behalf and on Your sole responsibility, not on behalf\n   of any other Contributor, and only if You agree to indemnify,\n   defend, and hold each Contributor harmless for any liability\n   incurred by, or claims asserted against, such Contributor by reason\n   of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work.\n\n   To apply the Apache License to your work, attach the following\n   boilerplate notice, with the fields enclosed by brackets \"[]\"\n   replaced with your own identifying information. (Don't include\n   the brackets!)  The text should be enclosed in the appropriate\n   comment syntax for the file format. We also recommend that a\n   file or class name and description of purpose be included on the\n   same \"printed page\" as the copyright notice for easier\n   identification within third-party archives.\n\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n\thttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# dingo-hunter [![Build Status](https://travis-ci.org/nickng/dingo-hunter.svg?branch=master)](https://travis-ci.org/nickng/dingo-hunter)\n\n## Static analyser for finding Deadlocks in Go\n\nThis is a static analyser to model concurrency and find deadlocks in Go code.\nThe main purpose of this tool is to infer from Go source code its concurrency\nmodel in either of the two formats:\n\n - Communicating Finite State Machines (CFSMs)\n - MiGo types\n\nThe inferred models are then passed to separate tools for formal analysis.\nIn both approaches, we apply a system known in the literature as\n[**Session Types**](http://mrg.doc.ic.ac.uk/publications/multiparty-asynchronous-session-types/)\nto look for potential communication mismatches to preempt potential deadlocks.\n\n## Install\n\n`dingo-hunter` can be installed by `go get`, go version `go1.7.1` is required.\n\n    $ go get -u github.com/nickng/dingo-hunter\n\n## Usage\n\nThere are two approaches (CFSMs and MiGo types) based on two research work.\n\n### CFSMs approach\n\nThis approach generates CFSMs as models for goroutines spawned in the program,\nthe CFSMs are then passed to a synthesis tool to construct a global choreography\nand check for validity (See [paper][cc16]).\n\nFirst install the synthesis tool `gmc-synthesis` by checking out the submodule:\n\n    $ cd $GOPATH/src/github.com/nickng/dingo-hunter; git submodule init; git submodule update\n    $ cd third_party/gmc-synthesis\n\nFollow `README` to install and build `gmc-synthesis`, i.e.\n\n    $ cabal install MissingH split Graphalyze\n    $ ./getpetrify # and install to /usr/local/ or in $PATH\n    $ ghc -threaded GMC.hs --make && ghc --make BuildGlobal\n\nTo run CFSMs generation on `example/local-deadlock/main.go`:\n\n    $ dingo-hunter cfsms --prefix deadlock example/local-deadlock/main.go\n\nOutput should say 2 channels, then run synthesis tool on extracted CFSMs\n\n    $ cd third_party/gmc-synthesis\n    $ ./runsmc inputs/deadlock_cfsms 2 # where 2 is the number of channels\n\nThe `SMC check` line indicates if the global graph satisfies SMC (i.e. safe) or not.\n\n#### Limitations\n\n  * Our tool currently support synchronous (unbuffered channel) communication only\n  * Goroutines spawned after any communication operations must not depend on\n    those communication. Our model assumes goroutines are spawned independenly.\n\n### MiGo types approach\n\nThis approach generates MiGo types, a behavioural type introduced in [this work][popl17],\nto check for safety and liveness by a restriction called *fencing* on channel\nusage (See [paper][popl17]).\n\nThe checker for MiGo types is available at\n[nickng/gong](https://github.com/nickng/gong), follow the instructions to build\nthe tool:\n\n    $ git clone ssh://github.com/nickng/gong.git\n    $ cd gong; ghc Gong.hs\n\nTo run MiGo types generation on `example/local-deadlock/main.go`:\n\n    $ dingo-hunter migo example/local-deadlock/main.go --no-logging --output deadlock.migo\n    $ /path/to/Gong -A deadlock.migo\n\n#### Limitations\n\n  * Channels as return values are not supported right now\n  * Channel recv,ok test not possible to represent in MiGo (requires inspecting\n    value but abstracted by types)\n\n## Research publications\n\n  * [Static Deadlock Detection for Concurrent Go by Global Session Graph Synthesis][cc16],\n    Nicholas Ng and Nobuko Yoshida,\n    Int'l Conference on Compiler Construction (CC 2016), ACM\n  * [Fencing off Go: Liveness and Safety for Channel-based Programming][popl17],\n    Julien Lange, Nicholas Ng, Bernardo Toninho and Nobuko Yoshida,\n    ACM SIGPLAN Symposium on Principles of Programming Languages (POPL 2017), ACM\n\n## Notes\n\nThis is a research prototype, and may not work for all Go source code. Please\nfile an issue for problems that look like a bug.\n\n## License\n\n  dingo-hunter is licensed under the [Apache License](http://www.apache.org/licenses/LICENSE-2.0)\n\n[cc16]: http://dl.acm.org/citation.cfm?doid=2892208.2892232 \"Static Deadlock Detection for Concurrent Go by Global Graph Synthesis\"\n[popl17]: http://dl.acm.org/citation.cfm?doiid=3009837.3009847 \"Fencing off Go: Liveness and Safety for Channel-based Programming\"\n"
  },
  {
    "path": "cfsmextract/cfsmextract.go",
    "content": "package cfsmextract\n\n// This file contains only the functions needed to start the analysis\n//  - Handle command line flags\n//  - Set up session variables\n\nimport (\n\t\"fmt\"\n\t\"go/types\"\n\t\"log\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/nickng/dingo-hunter/cfsmextract/sesstype\"\n\t\"github.com/nickng/dingo-hunter/cfsmextract/utils\"\n\t\"github.com/nickng/dingo-hunter/ssabuilder\"\n\t\"golang.org/x/tools/go/ssa\"\n)\n\ntype CFSMExtract struct {\n\tSSA   *ssabuilder.SSAInfo\n\tTime  time.Duration\n\tDone  chan struct{}\n\tError chan error\n\n\tsession *sesstype.Session\n\tgoQueue []*frame\n\tprefix  string\n\toutdir  string\n}\n\nfunc New(ssainfo *ssabuilder.SSAInfo, prefix, outdir string) *CFSMExtract {\n\treturn &CFSMExtract{\n\t\tSSA:   ssainfo,\n\t\tDone:  make(chan struct{}),\n\t\tError: make(chan error),\n\n\t\tsession: sesstype.CreateSession(),\n\t\tgoQueue: []*frame{},\n\t\tprefix:  prefix,\n\t\toutdir:  outdir,\n\t}\n}\n\n// Run function analyses main.main() then all the goroutines collected, and\n// finally output the analysis results.\nfunc (extract *CFSMExtract) Run() {\n\tstartTime := time.Now()\n\tmainPkg := ssabuilder.MainPkg(extract.SSA.Prog)\n\tif mainPkg == nil {\n\t\tfmt.Fprintf(os.Stderr, \"Error: 'main' package not found\\n\")\n\t\tos.Exit(1)\n\t}\n\tinit := mainPkg.Func(\"init\")\n\tmain := mainPkg.Func(\"main\")\n\tfr := makeToplevelFrame(extract)\n\tfor _, pkg := range extract.SSA.Prog.AllPackages() {\n\t\tfor _, memb := range pkg.Members {\n\t\t\tswitch val := memb.(type) {\n\t\t\tcase *ssa.Global:\n\t\t\t\tswitch derefAll(val.Type()).(type) {\n\t\t\t\tcase *types.Array:\n\t\t\t\t\tvd := utils.NewDef(val)\n\t\t\t\t\tfr.env.globals[val] = vd\n\t\t\t\t\tfr.env.arrays[vd] = make(Elems)\n\n\t\t\t\tcase *types.Struct:\n\t\t\t\t\tvd := utils.NewDef(val)\n\t\t\t\t\tfr.env.globals[val] = vd\n\t\t\t\t\tfr.env.structs[vd] = make(Fields)\n\n\t\t\t\tcase *types.Chan:\n\t\t\t\t\tvar c *types.Chan\n\t\t\t\t\tvd := utils.NewDef(utils.EmptyValue{T: c})\n\t\t\t\t\tfr.env.globals[val] = vd\n\n\t\t\t\tdefault:\n\t\t\t\t\tfr.env.globals[val] = utils.NewDef(val)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfmt.Fprintf(os.Stderr, \"++ call.toplevel %s()\\n\", orange(\"init\"))\n\tvisitFunc(init, fr)\n\tif main == nil {\n\t\tfmt.Fprintf(os.Stderr, \"Error: 'main()' function not found in 'main' package\\n\")\n\t\tos.Exit(1)\n\t}\n\tfmt.Fprintf(os.Stderr, \"++ call.toplevel %s()\\n\", orange(\"main\"))\n\tvisitFunc(main, fr)\n\n\tfr.env.session.Types[fr.gortn.role] = fr.gortn.root\n\n\tvar goFrm *frame\n\tfor len(extract.goQueue) > 0 {\n\t\tgoFrm, extract.goQueue = extract.goQueue[0], extract.goQueue[1:]\n\t\tfmt.Fprintf(os.Stderr, \"\\n%s\\nLOCATION: %s%s\\n\", goFrm.fn.Name(), goFrm.gortn.role.Name(), loc(goFrm, goFrm.fn.Pos()))\n\t\tvisitFunc(goFrm.fn, goFrm)\n\t\tgoFrm.env.session.Types[goFrm.gortn.role] = goFrm.gortn.root\n\t}\n\n\textract.Time = time.Since(startTime)\n\textract.Done <- struct{}{}\n}\n\n// Session returns the session after extraction.\nfunc (extract *CFSMExtract) Session() *sesstype.Session {\n\treturn extract.session\n}\n\nfunc (extract *CFSMExtract) WriteOutput() {\n\tfmt.Printf(\" ----- Results ----- \\n%s\\n\", extract.session.String())\n\n\tsesstype.PrintNodeSummary(extract.session)\n\n\tdotFile, err := os.OpenFile(fmt.Sprintf(\"%s.dot\", extract.prefix), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer dotFile.Close()\n\n\tdot := sesstype.NewGraphvizDot(extract.session)\n\tif _, err = dot.WriteTo(dotFile); err != nil {\n\t\tpanic(err)\n\t}\n\n\tos.MkdirAll(extract.outdir, 0750)\n\tcfsmPath := fmt.Sprintf(\"%s/%s_cfsms\", extract.outdir, extract.prefix)\n\tcfsmFile, err := os.OpenFile(cfsmPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer cfsmFile.Close()\n\n\tcfsms := sesstype.NewCFSMs(extract.session)\n\tif _, err := cfsms.WriteTo(cfsmFile); err != nil {\n\t\tlog.Fatalf(\"Cannot write CFSMs to file: %v\", err)\n\t}\n\n\tfmt.Fprintf(os.Stderr, \"CFSMs written to %s\\n\", cfsmPath)\n\tcfsms.PrintSummary()\n}\n"
  },
  {
    "path": "cfsmextract/func.go",
    "content": "package cfsmextract\n\nimport (\n\t\"fmt\"\n\t\"go/types\"\n\t\"os\"\n\n\t\"github.com/nickng/dingo-hunter/cfsmextract/sesstype\"\n\t\"github.com/nickng/dingo-hunter/cfsmextract/utils\"\n\t\"golang.org/x/tools/go/ssa\"\n)\n\n// VarKind specifies the type of utils.VarDef used in frame.local[]\ntype VarKind int\n\n// VarKind definitions\nconst (\n\tNothing     VarKind = iota // Not in frame.local[]\n\tArray                      // Array in heap\n\tChan                       // Channel\n\tStruct                     // Struct in heap\n\tLocalArray                 // Array in frame\n\tLocalStruct                // Struct in frame\n\tUntracked                  // Not a tracked VarKind but in frame.local[]\n)\n\n// Captures are lists of VarDefs for closure captures\ntype Captures []*utils.Definition\n\n// Tuples are lists of VarDefs from multiple return values\ntype Tuples []*utils.Definition\n\n// Elems are maps from array indices (variable) to VarDefs\ntype Elems map[ssa.Value]*utils.Definition\n\n// Fields are maps from struct fields (integer) to VarDefs\ntype Fields map[int]*utils.Definition\n\n// Frame holds variables in current function scope\ntype frame struct {\n\tfn      *ssa.Function                   // Function ptr of callee\n\tlocals  map[ssa.Value]*utils.Definition // Holds definitions of local registers\n\tarrays  map[*utils.Definition]Elems     // Array elements (Alloc local)\n\tstructs map[*utils.Definition]Fields    // Struct fields (Alloc local)\n\ttuples  map[ssa.Value]Tuples            // Multiple return values as tuple\n\tphi     map[ssa.Value][]ssa.Value       // Phis\n\trecvok  map[ssa.Value]*sesstype.Chan    // Channel used in recvok\n\tretvals Tuples                          // Return values to pass back to parent\n\tdefers  []*ssa.Defer                    // Deferred calls\n\tcaller  *frame                          // Ptr to caller's frame, nil if main/ext\n\tenv     *environ                        // Environment\n\tgortn   *goroutine                      // Current goroutine\n}\n\n// Environment: Variables/info available globally for all goroutines\ntype environ struct {\n\tsession  *sesstype.Session\n\textract  *CFSMExtract\n\tglobals  map[ssa.Value]*utils.Definition      // Globals\n\tarrays   map[*utils.Definition]Elems          // Array elements\n\tstructs  map[*utils.Definition]Fields         // Struct fields\n\tchans    map[*utils.Definition]*sesstype.Chan // Channels\n\textern   map[ssa.Value]types.Type             // Values that originates externally, we are only sure of its type\n\tclosures map[ssa.Value]Captures               // Closure captures\n\tselNode  map[ssa.Value]struct {               // Parent nodes of select\n\t\tparent   *sesstype.Node\n\t\tblocking bool\n\t}\n\tselIdx  map[ssa.Value]ssa.Value // Mapping from select index to select SSA Value\n\tselTest map[ssa.Value]struct {  // Records test for select-branch index\n\t\tidx int       // The index of the branch\n\t\ttpl ssa.Value // The SelectState tuple which the branch originates from\n\t}\n\trecvTest map[ssa.Value]*sesstype.Chan // Receive test\n\tifparent *sesstype.NodeStack\n}\n\nfunc (env *environ) GetSessionChan(vd *utils.Definition) *sesstype.Chan {\n\tif ch, ok := env.session.Chans[vd]; ok {\n\t\treturn &ch\n\t}\n\tpanic(fmt.Sprintf(\"Channel %s undefined in session\", vd.String()))\n}\n\nfunc makeToplevelFrame(extract *CFSMExtract) *frame {\n\tcallee := &frame{\n\t\tfn:      nil,\n\t\tlocals:  make(map[ssa.Value]*utils.Definition),\n\t\tarrays:  make(map[*utils.Definition]Elems),\n\t\tstructs: make(map[*utils.Definition]Fields),\n\t\ttuples:  make(map[ssa.Value]Tuples),\n\t\tphi:     make(map[ssa.Value][]ssa.Value),\n\t\trecvok:  make(map[ssa.Value]*sesstype.Chan),\n\t\tretvals: make(Tuples, 0),\n\t\tdefers:  make([]*ssa.Defer, 0),\n\t\tcaller:  nil,\n\t\tenv: &environ{\n\t\t\tsession:  extract.session,\n\t\t\textract:  extract,\n\t\t\tglobals:  make(map[ssa.Value]*utils.Definition),\n\t\t\tarrays:   make(map[*utils.Definition]Elems),\n\t\t\tstructs:  make(map[*utils.Definition]Fields),\n\t\t\tchans:    make(map[*utils.Definition]*sesstype.Chan),\n\t\t\textern:   make(map[ssa.Value]types.Type),\n\t\t\tclosures: make(map[ssa.Value]Captures),\n\t\t\tselNode: make(map[ssa.Value]struct {\n\t\t\t\tparent   *sesstype.Node\n\t\t\t\tblocking bool\n\t\t\t}),\n\t\t\tselIdx: make(map[ssa.Value]ssa.Value),\n\t\t\tselTest: make(map[ssa.Value]struct {\n\t\t\t\tidx int\n\t\t\t\ttpl ssa.Value\n\t\t\t}),\n\t\t\trecvTest: make(map[ssa.Value]*sesstype.Chan),\n\t\t\tifparent: sesstype.NewNodeStack(),\n\t\t},\n\t\tgortn: &goroutine{\n\t\t\trole:    extract.session.GetRole(\"main\"),\n\t\t\troot:    sesstype.NewLabelNode(\"main\"),\n\t\t\tleaf:    nil,\n\t\t\tvisited: make(map[*ssa.BasicBlock]sesstype.Node),\n\t\t},\n\t}\n\tcallee.gortn.leaf = &callee.gortn.root\n\n\treturn callee\n}\n\nfunc (caller *frame) callBuiltin(common *ssa.CallCommon) {\n\tbuiltin := common.Value.(*ssa.Builtin)\n\tif builtin.Name() == \"close\" {\n\t\tif len(common.Args) == 1 {\n\t\t\tif ch, ok := caller.env.chans[caller.locals[common.Args[0]]]; ok {\n\t\t\t\tfmt.Fprintf(os.Stderr, \"++ call builtin %s(%s channel %s)\\n\", orange(builtin.Name()), green(common.Args[0].Name()), ch.Name())\n\t\t\t\tvisitClose(*ch, caller)\n\t\t\t} else {\n\t\t\t\tpanic(\"Builtin close() called with non-channel\\n\")\n\t\t\t}\n\t\t}\n\t} else if builtin.Name() == \"copy\" {\n\t\tdst := common.Args[0]\n\t\tsrc := common.Args[1]\n\t\tfmt.Fprintf(os.Stderr, \"++ call builtin %s(%s <- %s)\\n\", orange(\"copy\"), dst.Name(), src.Name())\n\t\tcaller.locals[dst] = caller.locals[src]\n\t\treturn\n\t} else {\n\t\tfmt.Fprintf(os.Stderr, \"++ call builtin %s(\", builtin.Name())\n\t\tfor _, arg := range common.Args {\n\t\t\tfmt.Fprintf(os.Stderr, \"%s\", arg.Name())\n\t\t}\n\t\tfmt.Fprintf(os.Stderr, \") # TODO (handle builtin)\\n\")\n\t}\n}\n\nfunc (caller *frame) call(c *ssa.Call) {\n\tcaller.callCommon(c, c.Common())\n}\n\nfunc (caller *frame) callCommon(call *ssa.Call, common *ssa.CallCommon) {\n\tswitch fn := common.Value.(type) {\n\tcase *ssa.Builtin:\n\t\tcaller.callBuiltin(common)\n\n\tcase *ssa.MakeClosure:\n\t\t// TODO(nickng) Handle calling closure\n\t\tfmt.Fprintf(os.Stderr, \"   # TODO (handle closure) %s\\n\", fn.String())\n\n\tcase *ssa.Function:\n\t\tif common.StaticCallee() == nil {\n\t\t\tpanic(\"Call with nil CallCommon!\")\n\t\t}\n\n\t\tcallee := &frame{\n\t\t\tfn:      common.StaticCallee(),\n\t\t\tlocals:  make(map[ssa.Value]*utils.Definition),\n\t\t\tarrays:  make(map[*utils.Definition]Elems),\n\t\t\tstructs: make(map[*utils.Definition]Fields),\n\t\t\ttuples:  make(map[ssa.Value]Tuples),\n\t\t\tphi:     make(map[ssa.Value][]ssa.Value),\n\t\t\trecvok:  make(map[ssa.Value]*sesstype.Chan),\n\t\t\tretvals: make(Tuples, common.Signature().Results().Len()),\n\t\t\tdefers:  make([]*ssa.Defer, 0),\n\t\t\tcaller:  caller,\n\t\t\tenv:     caller.env,   // Use the same env as caller\n\t\t\tgortn:   caller.gortn, // Use the same role as caller\n\t\t}\n\n\t\tfmt.Fprintf(os.Stderr, \"++ call %s(\", orange(common.StaticCallee().String()))\n\t\tcallee.translate(common)\n\t\tfmt.Fprintf(os.Stderr, \")\\n\")\n\n\t\tif callee.isRecursive() {\n\t\t\tfmt.Fprintf(os.Stderr, \"-- Recursive %s()\\n\", orange(common.StaticCallee().String()))\n\t\t\tcallee.printCallStack()\n\t\t} else {\n\t\t\tif hasCode := visitFunc(callee.fn, callee); hasCode {\n\t\t\t\tcaller.handleRetvals(call.Value(), callee)\n\t\t\t} else {\n\t\t\t\tcaller.handleExtRetvals(call.Value(), callee)\n\t\t\t}\n\t\t\tfmt.Fprintf(os.Stderr, \"-- return from %s (%d retvals)\\n\", orange(common.StaticCallee().String()), len(callee.retvals))\n\t\t}\n\n\tdefault:\n\t\tif !common.IsInvoke() {\n\t\t\tfmt.Fprintf(os.Stderr, \"Unknown call type %v\\n\", common)\n\t\t\treturn\n\t\t}\n\n\t\tswitch vd, kind := caller.get(common.Value); kind {\n\t\tcase Struct, LocalStruct:\n\t\t\tfmt.Fprintf(os.Stderr, \"++ invoke %s.%s, type=%s\\n\", reg(common.Value), common.Method.String(), vd.Var.Type().String())\n\t\t\t// If dealing with interfaces, check that the method is invokable\n\t\t\tif iface, ok := common.Value.Type().Underlying().(*types.Interface); ok {\n\t\t\t\tif meth, _ := types.MissingMethod(vd.Var.Type(), iface, true); meth != nil {\n\t\t\t\t\tfmt.Fprintf(os.Stderr, \"     ^ interface not fully implemented\\n\")\n\t\t\t\t} else {\n\t\t\t\t\tfn := findMethod(common.Value.Parent().Prog, common.Method, vd.Var.Type())\n\t\t\t\t\tif fn != nil {\n\t\t\t\t\t\tfmt.Fprintf(os.Stderr, \"     ^ found function %s\\n\", fn.String())\n\n\t\t\t\t\t\tcallee := &frame{\n\t\t\t\t\t\t\tfn:      fn,\n\t\t\t\t\t\t\tlocals:  make(map[ssa.Value]*utils.Definition),\n\t\t\t\t\t\t\tarrays:  make(map[*utils.Definition]Elems),\n\t\t\t\t\t\t\tstructs: make(map[*utils.Definition]Fields),\n\t\t\t\t\t\t\ttuples:  make(map[ssa.Value]Tuples),\n\t\t\t\t\t\t\tphi:     make(map[ssa.Value][]ssa.Value),\n\t\t\t\t\t\t\trecvok:  make(map[ssa.Value]*sesstype.Chan),\n\t\t\t\t\t\t\tretvals: make(Tuples, common.Signature().Results().Len()),\n\t\t\t\t\t\t\tdefers:  make([]*ssa.Defer, 0),\n\t\t\t\t\t\t\tcaller:  caller,\n\t\t\t\t\t\t\tenv:     caller.env,   // Use the same env as caller\n\t\t\t\t\t\t\tgortn:   caller.gortn, // Use the same role as caller\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tcommon.Args = append([]ssa.Value{common.Value}, common.Args...)\n\t\t\t\t\t\tfmt.Fprintf(os.Stderr, \"++ call %s(\", orange(fn.String()))\n\t\t\t\t\t\tcallee.translate(common)\n\t\t\t\t\t\tfmt.Fprintf(os.Stderr, \")\\n\")\n\n\t\t\t\t\t\tif callee.isRecursive() {\n\t\t\t\t\t\t\tfmt.Fprintf(os.Stderr, \"-- Recursive %s()\\n\", orange(fn.String()))\n\t\t\t\t\t\t\tcallee.printCallStack()\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif hasCode := visitFunc(callee.fn, callee); hasCode {\n\t\t\t\t\t\t\t\tcaller.handleRetvals(call.Value(), callee)\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tcaller.handleExtRetvals(call.Value(), callee)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tfmt.Fprintf(os.Stderr, \"-- return from %s (%d retvals)\\n\", orange(fn.String()), len(callee.retvals))\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else {\n\t\t\t\t\t\tpanic(fmt.Sprintf(\"Cannot call function: %s.%s is abstract (program not well-formed)\", common.Value, common.Method.String()))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfmt.Fprintf(os.Stderr, \"     ^ method %s.%s does not exist\\n\", reg(common.Value), common.Method.String())\n\t\t\t}\n\n\t\tdefault:\n\t\t\tfmt.Fprintf(os.Stderr, \"++ invoke %s.%s\\n\", reg(common.Value), common.Method.String())\n\t\t}\n\t}\n}\n\nfunc findMethod(prog *ssa.Program, meth *types.Func, typ types.Type) *ssa.Function {\n\tif meth != nil {\n\t\tfmt.Fprintf(os.Stderr, \"     ^ finding method for type: %s pkg: %s name: %s\\n\", typ.String(), meth.Pkg().Name(), meth.Name())\n\t}\n\treturn prog.LookupMethod(typ, meth.Pkg(), meth.Name())\n}\n\nfunc (caller *frame) callGo(g *ssa.Go) {\n\tcommon := g.Common()\n\tgoname := fmt.Sprintf(\"%s_%d\", common.Value.Name(), int(g.Pos()))\n\tgorole := caller.env.session.GetRole(goname)\n\n\tcallee := &frame{\n\t\tfn:      common.StaticCallee(),\n\t\tlocals:  make(map[ssa.Value]*utils.Definition),\n\t\tarrays:  make(map[*utils.Definition]Elems),\n\t\tstructs: make(map[*utils.Definition]Fields),\n\t\ttuples:  make(map[ssa.Value]Tuples),\n\t\tphi:     make(map[ssa.Value][]ssa.Value),\n\t\trecvok:  make(map[ssa.Value]*sesstype.Chan),\n\t\tretvals: make(Tuples, common.Signature().Results().Len()),\n\t\tdefers:  make([]*ssa.Defer, 0),\n\t\tcaller:  caller,\n\t\tenv:     caller.env, // Use the same env as caller\n\t\tgortn: &goroutine{\n\t\t\trole:    gorole,\n\t\t\troot:    sesstype.NewLabelNode(goname),\n\t\t\tleaf:    nil,\n\t\t\tvisited: make(map[*ssa.BasicBlock]sesstype.Node),\n\t\t},\n\t}\n\tcallee.gortn.leaf = &callee.gortn.root\n\n\tfmt.Fprintf(os.Stderr, \"@@ queue go %s(\", common.StaticCallee().String())\n\tcallee.translate(common)\n\tfmt.Fprintf(os.Stderr, \")\\n\")\n\n\t// TODO(nickng) Does not stop at recursive call.\n\tcaller.env.extract.goQueue = append(caller.env.extract.goQueue, callee)\n}\n\nfunc (callee *frame) translate(common *ssa.CallCommon) {\n\tfor i, param := range callee.fn.Params {\n\t\targParent := common.Args[i]\n\t\tif param != argParent {\n\t\t\tif vd, ok := callee.caller.locals[argParent]; ok {\n\t\t\t\tcallee.locals[param] = vd\n\t\t\t}\n\t\t}\n\n\t\tif i > 0 {\n\t\t\tfmt.Fprintf(os.Stderr, \", \")\n\t\t}\n\n\t\tfmt.Fprintf(os.Stderr, \"%s:caller[%s] = %s\", orange(param.Name()), reg(common.Args[i]), callee.locals[param].String())\n\t\tmyVD := callee.locals[param] // VD of parameter (which are in callee.locals)\n\n\t\t// if argument is a channel\n\t\tif ch, ok := callee.env.chans[myVD]; ok {\n\t\t\tfmt.Fprintf(os.Stderr, \" channel %s\", (*ch).Name())\n\t\t} else if _, ok := callee.env.structs[myVD]; ok {\n\t\t\tfmt.Fprintf(os.Stderr, \" struct\")\n\t\t} else if _, ok := callee.env.arrays[myVD]; ok {\n\t\t\tfmt.Fprintf(os.Stderr, \" array\")\n\t\t} else if fields, ok := callee.caller.structs[myVD]; ok {\n\t\t\t// If param is local struct in caller, make local copy\n\t\t\tfmt.Fprintf(os.Stderr, \" lstruct\")\n\t\t\tcallee.structs[myVD] = fields\n\t\t} else if elems, ok := callee.caller.arrays[myVD]; ok {\n\t\t\t// If param is local array in caller, make local copy\n\t\t\tfmt.Fprintf(os.Stderr, \" larray\")\n\t\t\tcallee.arrays[myVD] = elems\n\t\t}\n\t}\n\n\t// Closure capture (copy from env.closures assigned in MakeClosure).\n\tif captures, isClosure := callee.env.closures[common.Value]; isClosure {\n\t\tfor idx, fv := range callee.fn.FreeVars {\n\t\t\tcallee.locals[fv] = captures[idx]\n\t\t\tfmt.Fprintf(os.Stderr, \", capture %s = %s\", fv.Name(), captures[idx].String())\n\t\t}\n\t}\n}\n\n// handleRetvals looks up and stores return value from function calls.\n// Nothing will be done if there are no return values from the function.\nfunc (caller *frame) handleRetvals(returned ssa.Value, callee *frame) {\n\tif len(callee.retvals) > 0 {\n\t\tif len(callee.retvals) == 1 {\n\t\t\t// Single return value (callee.retvals[0])\n\t\t\tcaller.locals[returned] = callee.retvals[0]\n\t\t} else {\n\t\t\t// Multiple return values (callee.retvals tuple)\n\t\t\tcaller.tuples[returned] = callee.retvals\n\t\t}\n\t}\n}\n\nfunc (callee *frame) get(v ssa.Value) (*utils.Definition, VarKind) {\n\tif vd, ok := callee.locals[v]; ok {\n\t\tif _, ok := callee.env.arrays[vd]; ok {\n\t\t\treturn vd, Array\n\t\t}\n\t\tif _, ok := callee.arrays[vd]; ok {\n\t\t\treturn vd, LocalArray\n\t\t}\n\t\tif _, ok := callee.env.chans[vd]; ok {\n\t\t\treturn vd, Chan\n\t\t}\n\t\tif _, ok := callee.env.structs[vd]; ok {\n\t\t\treturn vd, Struct\n\t\t}\n\t\tif _, ok := callee.structs[vd]; ok {\n\t\t\treturn vd, LocalStruct\n\t\t}\n\t\treturn vd, Untracked\n\t} else if vs, ok := callee.phi[v]; ok {\n\t\tfor i := len(vs) - 1; i >= 0; i-- {\n\t\t\tif chVd, defined := callee.locals[vs[i]]; defined {\n\t\t\t\treturn chVd, Chan\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, Nothing\n}\n\n// handleExtRetvals looks up and stores return value from (ext) function calls.\n// Ext functions have no code (no body to analyse) and unlike normal values,\n// the return values/tuples are stored until they are referenced.\nfunc (caller *frame) handleExtRetvals(returned ssa.Value, callee *frame) {\n\t// Since there are no code for the function, we use the function\n\t// signature to see if any of these are channels.\n\t// XXX We don't know where these come from so we put them in extern.\n\tresultsLen := callee.fn.Signature.Results().Len()\n\tif resultsLen > 0 {\n\t\tcaller.env.extern[returned] = callee.fn.Signature.Results()\n\t\tif resultsLen == 1 {\n\t\t\tfmt.Fprintf(os.Stderr, \"-- Return from %s (builtin/ext) with a single value\\n\", callee.fn.String())\n\t\t\tif _, ok := callee.fn.Signature.Results().At(0).Type().(*types.Chan); ok {\n\t\t\t\tvardef := utils.NewDef(returned)\n\t\t\t\tch := caller.env.session.MakeExtChan(vardef, caller.gortn.role)\n\t\t\t\tcaller.env.chans[vardef] = &ch\n\t\t\t\tfmt.Fprintf(os.Stderr, \"-- Return value from %s (builtin/ext) is a channel %s (ext)\\n\", callee.fn.String(), (*caller.env.chans[vardef]).Name())\n\t\t\t}\n\t\t} else {\n\t\t\tfmt.Fprintf(os.Stderr, \"-- Return from %s (builtin/ext) with %d-tuple\\n\", callee.fn.String(), resultsLen)\n\t\t}\n\t}\n}\n\nfunc (callee *frame) isRecursive() bool {\n\tvar tracebackFns []*ssa.Function\n\tfoundFr := callee\n\tfor fr := callee.caller; fr != nil; fr = fr.caller {\n\t\ttracebackFns = append(tracebackFns, fr.fn)\n\t\tif fr.fn == callee.fn {\n\t\t\tfoundFr = fr\n\t\t\tbreak\n\t\t}\n\t}\n\t// If same function is not found, not recursive\n\tif foundFr == callee {\n\t\treturn false\n\t}\n\n\t// Otherwise try to trace back with foundFr and is recursive if all matches\n\tfor _, fn := range tracebackFns {\n\t\tif foundFr == nil || foundFr.fn != fn {\n\t\t\treturn false\n\t\t}\n\t\tfoundFr = foundFr.caller\n\t}\n\treturn true\n}\n\nfunc (callee *frame) printCallStack() {\n\tcurFr := callee\n\tfor curFr != nil && curFr.fn != nil {\n\t\tfmt.Fprintf(os.Stderr, \"Called by: %s()\\n\", curFr.fn.String())\n\t\tcurFr = curFr.caller\n\t}\n}\n\nfunc (callee *frame) updateDefs(vdOld, vdNew *utils.Definition) {\n\tfor def, array := range callee.arrays {\n\t\tfor k, v := range array {\n\t\t\tif v == vdOld {\n\t\t\t\tcallee.arrays[def][k] = vdNew\n\t\t\t}\n\t\t}\n\t}\n\tfor def, array := range callee.env.arrays {\n\t\tfor k, v := range array {\n\t\t\tif v == vdOld {\n\t\t\t\tcallee.env.arrays[def][k] = vdNew\n\t\t\t}\n\t\t}\n\t}\n\tfor def, struc := range callee.structs {\n\t\tfor i, field := range struc {\n\t\t\tif field == vdOld {\n\t\t\t\tcallee.structs[def][i] = vdNew\n\t\t\t}\n\t\t}\n\t}\n\tfor def, struc := range callee.env.structs {\n\t\tfor i, field := range struc {\n\t\t\tif field == vdOld {\n\t\t\t\tcallee.env.structs[def][i] = vdNew\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "cfsmextract/gortn.go",
    "content": "package cfsmextract\n\nimport (\n\t\"github.com/nickng/dingo-hunter/cfsmextract/sesstype\"\n\t\"golang.org/x/tools/go/ssa\"\n)\n\ntype goroutine struct {\n\trole    sesstype.Role\n\troot    sesstype.Node\n\tleaf    *sesstype.Node\n\tvisited map[*ssa.BasicBlock]sesstype.Node\n}\n\n// Append a session type node to current goroutine.\nfunc (gortn *goroutine) AddNode(node sesstype.Node) {\n\tif gortn.leaf == nil {\n\t\tpanic(\"AddNode: leaf cannot be nil\")\n\t}\n\n\tnewLeaf := (*gortn.leaf).Append(node)\n\tgortn.leaf = &newLeaf\n}\n"
  },
  {
    "path": "cfsmextract/sesstype/cfsm.go",
    "content": "package sesstype\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\n\t\"github.com/nickng/cfsm\"\n)\n\n// STOP is the 'close' message.\nconst STOP = \"STOP\"\n\n// CFSMs captures a CFSM system syserated from a Session.\ntype CFSMs struct {\n\tSys    *cfsm.System\n\tChans  map[Role]*cfsm.CFSM\n\tRoles  map[Role]*cfsm.CFSM\n\tStates map[*cfsm.CFSM]map[string]*cfsm.State\n}\n\nfunc NewCFSMs(s *Session) *CFSMs {\n\tsys := &CFSMs{\n\t\tSys:    cfsm.NewSystem(),\n\t\tChans:  make(map[Role]*cfsm.CFSM),\n\t\tRoles:  make(map[Role]*cfsm.CFSM),\n\t\tStates: make(map[*cfsm.CFSM]map[string]*cfsm.State),\n\t}\n\tfor _, c := range s.Chans {\n\t\tm := sys.Sys.NewMachine()\n\t\tm.Comment = c.Name()\n\t\tsys.Chans[c] = m\n\t\tdefer sys.chanToMachine(c, c.Type().String(), m)\n\t}\n\tfor role, root := range s.Types {\n\t\tm := sys.Sys.NewMachine()\n\t\tm.Comment = role.Name()\n\t\tsys.Roles[role] = m\n\t\tsys.States[m] = make(map[string]*cfsm.State)\n\t\tsys.rootToMachine(role, root, m)\n\t\tif m.IsEmpty() {\n\t\t\tlog.Println(\"Machine\", m.ID, \"is empty\")\n\t\t\tsys.Sys.RemoveMachine(m.ID)\n\t\t\tdelete(sys.Roles, role)\n\t\t\tdelete(sys.States, m)\n\t\t}\n\t}\n\treturn sys\n}\n\n// WriteTo implementers io.WriterTo interface.\nfunc (sys *CFSMs) WriteTo(w io.Writer) (int64, error) {\n\tn, err := w.Write([]byte(sys.Sys.String()))\n\treturn int64(n), err\n}\n\n// PrintSummary shows the statistics of the CFSM syseration.\nfunc (sys *CFSMs) PrintSummary() {\n\tfmt.Printf(\"Total of %d CFSMs (%d are channels)\\n\",\n\t\tlen(sys.Roles)+len(sys.Chans), len(sys.Chans))\n\tfor r, m := range sys.Chans {\n\t\tfmt.Printf(\"\\t%d\\t= %s (channel)\\n\", m.ID, r.Name())\n\t}\n\tfor r, m := range sys.Roles {\n\t\tfmt.Printf(\"\\t%d\\t= %s\\n\", m.ID, r.Name())\n\t}\n}\n\nfunc (sys *CFSMs) rootToMachine(role Role, root Node, m *cfsm.CFSM) {\n\tq0 := m.NewState()\n\tsys.nodeToMachine(role, root, q0, m)\n\tm.Start = q0\n}\n\nfunc (sys *CFSMs) nodeToMachine(role Role, node Node, q0 *cfsm.State, m *cfsm.CFSM) {\n\tswitch node := node.(type) {\n\tcase *SendNode:\n\t\tto, ok := sys.Chans[node.To()]\n\t\tif !ok {\n\t\t\tlog.Fatal(\"Cannot Send to unknown channel\", node.To().Name())\n\t\t}\n\t\ttr := cfsm.NewSend(to, node.To().Type().String())\n\t\tvar qSent *cfsm.State\n\t\tif sys.isSelfLoop(m, q0, node) {\n\t\t\tqSent = q0\n\t\t} else {\n\t\t\tqSent = m.NewState()\n\t\t\tfor _, c := range node.Children() {\n\t\t\t\tsys.nodeToMachine(role, c, qSent, m)\n\t\t\t}\n\t\t}\n\t\ttr.SetNext(qSent)\n\t\tq0.AddTransition(tr)\n\n\tcase *RecvNode:\n\t\tfrom, ok := sys.Chans[node.From()]\n\t\tif !ok {\n\t\t\tlog.Fatal(\"Cannot Recv from unknown channel\", node.From().Name())\n\t\t}\n\t\tmsg := node.From().Type().String()\n\t\tif node.Stop() {\n\t\t\tmsg = STOP\n\t\t}\n\t\ttr := cfsm.NewRecv(from, msg)\n\t\tvar qRcvd *cfsm.State\n\t\tif sys.isSelfLoop(m, q0, node) {\n\t\t\tqRcvd = q0\n\t\t} else {\n\t\t\tqRcvd = m.NewState()\n\t\t\tfor _, c := range node.Children() {\n\t\t\t\tsys.nodeToMachine(role, c, qRcvd, m)\n\t\t\t}\n\t\t}\n\t\ttr.SetNext(qRcvd)\n\t\tq0.AddTransition(tr)\n\n\tcase *EndNode:\n\t\tch, ok := sys.Chans[node.Chan()]\n\t\tif !ok {\n\t\t\tlog.Fatal(\"Cannot Close unknown channel\", node.Chan().Name())\n\t\t}\n\t\ttr := cfsm.NewSend(ch, STOP)\n\t\tqEnd := m.NewState()\n\t\tfor _, c := range node.Children() {\n\t\t\tsys.nodeToMachine(role, c, qEnd, m)\n\t\t}\n\t\ttr.SetNext(qEnd)\n\t\tq0.AddTransition(tr)\n\n\tcase *NewChanNode, *EmptyBodyNode: // Skip\n\t\tfor _, c := range node.Children() {\n\t\t\tsys.nodeToMachine(role, c, q0, m)\n\t\t}\n\n\tcase *LabelNode:\n\t\tsys.States[m][node.Name()] = q0\n\t\tfor _, c := range node.Children() {\n\t\t\tsys.nodeToMachine(role, c, q0, m)\n\t\t}\n\n\tcase *GotoNode:\n\t\tqTarget := sys.States[m][node.Name()]\n\t\tfor _, c := range node.Children() {\n\t\t\tsys.nodeToMachine(role, c, qTarget, m)\n\t\t}\n\n\tdefault:\n\t\tlog.Fatalf(\"Unhandled node type %T\", node)\n\t}\n}\n\nfunc (sys *CFSMs) chanToMachine(ch Role, T string, m *cfsm.CFSM) {\n\tq0 := m.NewState()\n\tqEnd := m.NewState()\n\tfor _, machine := range sys.Roles {\n\t\tq1 := m.NewState()\n\t\t// q0 -- Recv --> q1\n\t\ttr0 := cfsm.NewRecv(machine, T)\n\t\ttr0.SetNext(q1)\n\t\tq0.AddTransition(tr0)\n\t\t// q1 -- Send --> q0\n\t\tfor _, machine2 := range sys.Roles {\n\t\t\tif machine.ID != machine2.ID {\n\t\t\t\ttr1 := cfsm.NewSend(machine2, T)\n\t\t\t\ttr1.SetNext(q0)\n\t\t\t\tq1.AddTransition(tr1)\n\t\t\t}\n\t\t}\n\t\t// q0 -- STOP --> qEnd (same qEnd)\n\t\ttr2 := cfsm.NewRecv(machine, STOP)\n\t\ttr2.SetNext(qEnd)\n\t\tqEnd.AddTransition(tr2)\n\t\t// qEnd -- STOP --> qEnd\n\t\tfor _, machine2 := range sys.Roles {\n\t\t\tif machine.ID != machine2.ID {\n\t\t\t\ttr3 := cfsm.NewSend(machine2, STOP)\n\t\t\t\ttr3.SetNext(qEnd)\n\t\t\t\tqEnd.AddTransition(tr3)\n\t\t\t}\n\t\t}\n\t}\n\tm.Start = q0\n}\n\n// isSelfLoop returns true if the action of node is a self-loop\n// i.e. the state before and after the transition is the same.\nfunc (sys *CFSMs) isSelfLoop(m *cfsm.CFSM, q0 *cfsm.State, node Node) bool {\n\tif len(node.Children()) == 1 {\n\t\tif gotoNode, ok := node.Child(0).(*GotoNode); ok {\n\t\t\tif loopback, ok := sys.States[m][gotoNode.Name()]; ok {\n\t\t\t\treturn loopback == q0\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "cfsmextract/sesstype/dot.go",
    "content": "package sesstype\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\n\t\"github.com/awalterschulze/gographviz\"\n)\n\n// GraphvizDot reprents a new graphviz dot graph.\ntype GraphvizDot struct {\n\tGraph      *gographviz.Escape\n\tCount      int\n\tLabelNodes map[string]string\n}\n\n// NewGraphvizDot creates a new graphviz dot graph from a session.\nfunc NewGraphvizDot(s *Session) *GraphvizDot {\n\tdot := &GraphvizDot{\n\t\tGraph:      gographviz.NewEscape(),\n\t\tCount:      0,\n\t\tLabelNodes: make(map[string]string),\n\t}\n\tdot.Graph.SetDir(true)\n\tdot.Graph.SetName(\"G\")\n\n\tfor role, root := range s.Types {\n\t\tsg := gographviz.NewSubGraph(\"\\\"cluster_\" + role.Name() + \"\\\"\")\n\t\tif root != nil {\n\t\t\tdot.visitNode(root, sg, nil)\n\t\t}\n\t\tdot.Graph.AddSubGraph(dot.Graph.Name, sg.Name, nil)\n\t}\n\treturn dot\n}\n\n// WriteTo implements io.WriterTo interface.\nfunc (dot *GraphvizDot) WriteTo(w io.Writer) (int64, error) {\n\tn, err := w.Write([]byte(dot.Graph.String()))\n\treturn int64(n), err\n}\n\nfunc (dot *GraphvizDot) nodeToDotNode(node Node) *gographviz.Node {\n\tswitch node := node.(type) {\n\tcase *LabelNode:\n\t\tdefer func() { dot.Count++ }()\n\t\tdot.LabelNodes[node.Name()] = fmt.Sprintf(\"label%d\", dot.Count)\n\t\tattrs, err := gographviz.NewAttrs(map[string]string{\n\t\t\t\"label\": fmt.Sprintf(\"\\\"%s\\\"\", node.String()),\n\t\t\t\"shape\": \"plaintext,\",\n\t\t})\n\t\tif err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\t\tdotNode := gographviz.Node{\n\t\t\tName:  dot.LabelNodes[node.Name()],\n\t\t\tAttrs: attrs,\n\t\t}\n\t\treturn &dotNode\n\n\tcase *NewChanNode:\n\t\tdefer func() { dot.Count++ }()\n\t\tattrs, err := gographviz.NewAttrs(map[string]string{\n\t\t\t\"label\": fmt.Sprintf(\"Channel %s Type:%s\", node.Chan().Name(), node.Chan().Type()),\n\t\t\t\"shape\": \"rect\",\n\t\t\t\"color\": \"red\",\n\t\t})\n\t\tif err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\t\treturn &gographviz.Node{\n\t\t\tName:  fmt.Sprintf(\"%s%d\", node.Kind(), dot.Count),\n\t\t\tAttrs: attrs,\n\t\t}\n\n\tcase *SendNode:\n\t\tdefer func() { dot.Count++ }()\n\t\tstyle := \"solid\"\n\t\tdesc := \"\"\n\t\tif node.IsNondet() {\n\t\t\tstyle = \"dashed\"\n\t\t\tdesc = \" nondet\"\n\t\t}\n\t\tattrs, err := gographviz.NewAttrs(map[string]string{\n\t\t\t\"label\": fmt.Sprintf(\"Send %s%s\", node.To().Name(), desc),\n\t\t\t\"shape\": \"rect\",\n\t\t\t\"style\": style,\n\t\t})\n\t\tif err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\t\treturn &gographviz.Node{\n\t\t\tName:  fmt.Sprintf(\"%s%d\", node.Kind(), dot.Count),\n\t\t\tAttrs: attrs,\n\t\t}\n\n\tcase *RecvNode:\n\t\tdefer func() { dot.Count++ }()\n\t\tstyle := \"solid\"\n\t\tdesc := \"\"\n\t\tif node.IsNondet() {\n\t\t\tstyle = \"dashed\"\n\t\t\tdesc = \" nondet\"\n\t\t}\n\t\tattrs, err := gographviz.NewAttrs(map[string]string{\n\t\t\t\"label\": fmt.Sprintf(\"Recv %s%s\", node.From().Name(), desc),\n\t\t\t\"shape\": \"rect\",\n\t\t\t\"style\": style,\n\t\t})\n\t\tif err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\t\treturn &gographviz.Node{\n\t\t\tName:  fmt.Sprintf(\"%s%d\", node.Kind(), dot.Count),\n\t\t\tAttrs: attrs,\n\t\t}\n\n\tcase *GotoNode:\n\t\treturn nil // No new node to create\n\n\tdefault:\n\t\tdefer func() { dot.Count++ }()\n\t\tattrs, err := gographviz.NewAttrs(map[string]string{\n\t\t\t\"label\": fmt.Sprintf(\"\\\"%s\\\"\", node.String()),\n\t\t\t\"shape\": \"rect\",\n\t\t})\n\t\tif err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\t\tdotNode := gographviz.Node{\n\t\t\tName:  fmt.Sprintf(\"%s%d\", node.Kind(), dot.Count),\n\t\t\tAttrs: attrs,\n\t\t}\n\t\treturn &dotNode\n\t}\n}\n\n// visitNode Creates a dot Node and from it create a subgraph of children.\n// Returns head of the subgraph.\nfunc (dot *GraphvizDot) visitNode(node Node, subgraph *gographviz.SubGraph, parent *gographviz.Node) *gographviz.Node {\n\tdotNode := dot.nodeToDotNode(node)\n\n\tif dotNode == nil { // GotoNode\n\t\tgtn := node.(*GotoNode)\n\t\tdot.Graph.AddEdge(parent.Name, dot.LabelNodes[gtn.Name()], true, nil)\n\t\tfor _, child := range node.Children() {\n\t\t\tdot.visitNode(child, subgraph, parent)\n\t\t}\n\t\treturn parent // GotoNode's children are children of parent. So return parent.\n\t}\n\n\tattrs := make(map[string]string)\n\tfor k, v := range dotNode.Attrs {\n\t\tattrs[string(k)] = v\n\t}\n\tdot.Graph.AddNode(subgraph.Name, dotNode.Name, attrs)\n\tif parent != nil { // Parent is not toplevel\n\t\tdot.Graph.AddEdge(parent.Name, dotNode.Name, true, nil)\n\t}\n\tfor _, child := range node.Children() {\n\t\tdot.visitNode(child, subgraph, dotNode)\n\t}\n\n\tif dotNode == nil {\n\t\tpanic(\"Cannot return nil dotNode\")\n\t}\n\n\treturn dotNode\n}\n"
  },
  {
    "path": "cfsmextract/sesstype/nodes.go",
    "content": "package sesstype\n\nimport (\n\t\"fmt\"\n)\n\nfunc CountNodes(root Node) int {\n\ttotal := 1\n\tfor _, child := range root.Children() {\n\t\ttotal += CountNodes(child)\n\t}\n\treturn total\n}\n\nfunc SessionCountNodes(session *Session) map[string]int {\n\tm := make(map[string]int)\n\tfor r, t := range session.Types {\n\t\tm[r.Name()] = CountNodes(t)\n\t}\n\treturn m\n}\n\nfunc PrintNodeSummary(session *Session) {\n\tcounts := SessionCountNodes(session)\n\tfmt.Printf(\"Total of nodes per role (%d roles)\\n\", len(counts))\n\tfor role, n := range counts {\n\t\tfmt.Printf(\"\\t%d\\t: %s\\n\", n, role)\n\t}\n}\n"
  },
  {
    "path": "cfsmextract/sesstype/nodestack.go",
    "content": "package sesstype // import \"github.com/nickng/dingo-hunter/cfsmextract/sesstype\"\n\n// NodeStack is a stack for sesstype.Node\ntype NodeStack struct {\n\tnodes []Node\n\tcount int\n}\n\n// Push pushes a sesstype.Node to the stack.\nfunc (s *NodeStack) Push(node Node) {\n\ts.nodes = append(s.nodes[:s.count], node)\n\ts.count++\n}\n\n// Pop removes a sesstype.Node from the stack.\nfunc (s *NodeStack) Pop() {\n\tif s.count <= 0 {\n\t\tpanic(\"Cannot pop empty stack\")\n\t}\n\ts.count--\n}\n\n// Top returns the sesstype.Node at the top of the stack.\nfunc (s *NodeStack) Top() Node {\n\tif s.count <= 0 {\n\t\treturn nil\n\t}\n\treturn s.nodes[s.count-1]\n}\n\n// Size returns number of sesstype.Node on the stack.\nfunc (s *NodeStack) Size() int {\n\treturn s.count\n}\n\n// String returns a String representing the stack.\nfunc (s *NodeStack) String() string {\n\tstr := \"[\"\n\tfor i := s.count - 1; i >= 0; i-- {\n\t\tstr += s.nodes[i].String()\n\t\tif i != 0 {\n\t\t\tstr += \", \"\n\t\t}\n\t}\n\tstr += \"]\"\n\treturn str\n}\n\n// NewNodeStack returns a new NodeStack instance.\nfunc NewNodeStack() *NodeStack {\n\treturn &NodeStack{}\n}\n"
  },
  {
    "path": "cfsmextract/sesstype/nodestack_test.go",
    "content": "package sesstype\n\nimport (\n\t\"testing\"\n)\n\nfunc TestNewStack(t *testing.T) {\n\tns := NewNodeStack()\n\tns.Push(NewLabelNode(\"TEST\"))\n\tns.Push(NewLabelNode(\"TEST2\"))\n\tns.Push(NewLabelNode(\"TEST3\"))\n\tl := ns.Top()\n\tif l.String() != \"TEST3\" {\n\t\tt.Fail()\n\t}\n\tns.Pop()\n\tl2 := ns.Top()\n\tif l2.String() != \"TEST2\" {\n\t\tt.Fail()\n\t}\n\tns.Pop()\n\tl3 := ns.Top()\n\tif l3.String() != \"TEST\" {\n\t\tt.Fail()\n\t}\n\tns.Pop()\n}\n"
  },
  {
    "path": "cfsmextract/sesstype/op_string.go",
    "content": "// generated by stringer -type=op; DO NOT EDIT\n\npackage sesstype\n\nimport \"fmt\"\n\nconst _op_name = \"NoOpNewChanOpSendOpRecvOpEndOp\"\n\nvar _op_index = [...]uint8{0, 4, 13, 19, 25, 30}\n\nfunc (i op) String() string {\n\tif i < 0 || i >= op(len(_op_index)-1) {\n\t\treturn fmt.Sprintf(\"op(%d)\", i)\n\t}\n\treturn _op_name[_op_index[i]:_op_index[i+1]]\n}\n"
  },
  {
    "path": "cfsmextract/sesstype/sesstype.go",
    "content": "// Package sesstype encapsulates representation of session types\n// As opposed to role-based session types, this representation is channel-based.\n// In particular, sending and receiving both keep track of the role and\n// channel involved.\npackage sesstype // import \"github.com/nickng/dingo-hunter/cfsmextract/sesstype\"\n\nimport (\n\t\"fmt\"\n\t\"go/types\"\n\n\t\"github.com/nickng/dingo-hunter/cfsmextract/utils\"\n\t\"golang.org/x/tools/go/ssa\"\n)\n\n//go:generate stringer -type=op\ntype op int\n\n// Chan is a typed channel in a session.\ntype Chan struct {\n\tdef    *utils.Definition\n\trole   Role\n\textern bool\n}\n\n// Return a name of channel.\nfunc (ch Chan) Name() string {\n\tfullname := fmt.Sprintf(\"%s\", ch.def.String())\n\tif ch.extern {\n\t\treturn fullname + \"*\"\n\t}\n\treturn fullname\n}\n\n// Return the payload type of channel.\nfunc (ch Chan) Type() types.Type {\n\tif c, ok := ch.def.Var.Type().(*types.Chan); ok {\n\t\treturn c.Elem()\n\t}\n\tpanic(\"Not channel \" + ch.def.Var.String())\n}\nfunc (ch Chan) Role() Role       { return ch.role }\nfunc (ch Chan) Value() ssa.Value { return ch.def.Var }\n\n// Role in a session (main or goroutine).\ntype Role interface {\n\tName() string\n}\n\ntype role struct {\n\tname string\n}\n\nfunc (r *role) Name() string { return r.name }\n\n// Different operations/actions available in session.\nconst (\n\tNoOp op = iota\n\tNewChanOp\n\tSendOp\n\tRecvOp\n\tEndOp\n)\n\n// A Node in the session graph.\ntype Node interface {\n\tKind() op               // For checking type without type switching\n\tChild(index int) Node   // Gets child at index\n\tAppend(child Node) Node // Returns new child for chaining\n\tChildren() []Node       // Returns whole slice\n\tString() string\n}\n\n// Session is a container of session graph nodes, also holds information about\n// channels and roles in the current session.\ntype Session struct {\n\tTypes map[Role]Node              // Root Node for each Role\n\tChans map[*utils.Definition]Chan // Actual channels are stored here\n\tRoles map[string]Role            // Actual roles are stored here\n}\n\n// CreateSession initialises a new empty Session.\nfunc CreateSession() *Session {\n\treturn &Session{\n\t\tTypes: make(map[Role]Node),\n\t\tChans: make(map[*utils.Definition]Chan),\n\t\tRoles: make(map[string]Role),\n\t}\n}\n\n// GetRole returns or create (if empty) a new session role using given name.\nfunc (s *Session) GetRole(name string) Role { // Get or create role\n\tif _, found := s.Roles[name]; !found {\n\t\ts.Roles[name] = &role{name: name}\n\t}\n\treturn s.Roles[name]\n}\n\n// MakeChan creates and stores a new session channel created.\nfunc (s *Session) MakeChan(v *utils.Definition, r Role) Chan {\n\ts.Chans[v] = Chan{\n\t\tdef:    v,\n\t\trole:   r,\n\t\textern: false,\n\t}\n\treturn s.Chans[v]\n}\n\n// MakeExtChan creates and stores a new channel and mark as externally created.\nfunc (s *Session) MakeExtChan(v *utils.Definition, r Role) Chan {\n\ts.Chans[v] = Chan{\n\t\tdef:    v,\n\t\trole:   r,\n\t\textern: true,\n\t}\n\treturn s.Chans[v]\n}\n\n// NewChanNode represents creation of new channel\ntype NewChanNode struct {\n\tch       Chan\n\tchildren []Node\n}\n\nfunc (nc *NewChanNode) Kind() op   { return NewChanOp }\nfunc (nc *NewChanNode) Chan() Chan { return nc.ch }\nfunc (nc *NewChanNode) Append(n Node) Node {\n\tnc.children = append(nc.children, n)\n\treturn n\n}\nfunc (nc *NewChanNode) Child(i int) Node { return nc.children[i] }\nfunc (nc *NewChanNode) Children() []Node { return nc.children }\nfunc (nc *NewChanNode) String() string {\n\treturn fmt.Sprintf(\"NewChan %s of type %s\", nc.ch.Name(), nc.ch.Type().String())\n}\n\n// SendNode represents a send.\ntype SendNode struct {\n\tsndr     Role       // Sender\n\tdest     Chan       // Destination\n\tnondet   bool       // Is this non-deterministic?\n\tt        types.Type // Datatype\n\tchildren []Node\n}\n\nfunc (s *SendNode) Kind() op       { return SendOp }\nfunc (s *SendNode) Sender() Role   { return s.sndr }\nfunc (s *SendNode) To() Chan       { return s.dest }\nfunc (s *SendNode) IsNondet() bool { return s.nondet }\nfunc (s *SendNode) Append(n Node) Node {\n\ts.children = append(s.children, n)\n\treturn n\n}\nfunc (s *SendNode) Child(i int) Node { return s.children[i] }\nfunc (s *SendNode) Children() []Node { return s.children }\nfunc (s *SendNode) String() string {\n\tvar nd string\n\tif s.nondet {\n\t\tnd = \"nondet \"\n\t}\n\treturn fmt.Sprintf(\"Send %s→ᶜʰ%s %s\", s.sndr.Name(), s.dest.Name(), nd)\n}\n\n// RecvNode represents a receive.\ntype RecvNode struct {\n\torig     Chan       // Originates from\n\trcvr     Role       // Received by\n\tnondet   bool       // Is this non-deterministic?\n\tt        types.Type // Datatype\n\tstop     bool       // Stop message only?\n\tchildren []Node\n}\n\nfunc (r *RecvNode) Kind() op       { return RecvOp }\nfunc (r *RecvNode) Receiver() Role { return r.rcvr }\nfunc (r *RecvNode) From() Chan     { return r.orig }\nfunc (r *RecvNode) IsNondet() bool { return r.nondet }\nfunc (r *RecvNode) Stop() bool     { return r.stop }\nfunc (r *RecvNode) Append(node Node) Node {\n\tr.children = append(r.children, node)\n\treturn node\n}\nfunc (r *RecvNode) Child(index int) Node { return r.children[index] }\nfunc (r *RecvNode) Children() []Node     { return r.children }\nfunc (r *RecvNode) String() string {\n\tvar nd string\n\tif r.nondet {\n\t\tnd = \"nondet \"\n\t}\n\tif r.t == nil {\n\t\treturn fmt.Sprintf(\"Recv END %s←ᶜʰ%s%s\", r.rcvr.Name(), r.orig.Name(), nd)\n\t}\n\treturn fmt.Sprintf(\"Recv %s←ᶜʰ%s%s\", r.rcvr.Name(), r.orig.Name(), nd)\n}\n\n// LabelNode makes a placeholder for loop/jumping\ntype LabelNode struct {\n\tname     string\n\tchildren []Node\n}\n\nfunc (l *LabelNode) Kind() op     { return NoOp }\nfunc (l *LabelNode) Name() string { return l.name }\nfunc (l *LabelNode) Append(n Node) Node {\n\tl.children = append(l.children, n)\n\treturn n\n}\nfunc (l *LabelNode) Child(i int) Node { return l.children[i] }\nfunc (l *LabelNode) Children() []Node { return l.children }\nfunc (l *LabelNode) String() string   { return fmt.Sprintf(\"%s\", l.name) }\n\n// GotoNode represents a jump (edge to existing LabelNode)\ntype GotoNode struct {\n\tname     string\n\tchildren []Node\n}\n\nfunc (g *GotoNode) Kind() op     { return NoOp }\nfunc (g *GotoNode) Name() string { return g.name }\nfunc (g *GotoNode) Append(n Node) Node {\n\tg.children = append(g.children, n)\n\treturn n\n}\nfunc (g *GotoNode) Child(i int) Node { return g.children[i] }\nfunc (g *GotoNode) Children() []Node { return g.children }\nfunc (g *GotoNode) String() string   { return fmt.Sprintf(\"Goto %s\", g.name) }\n\ntype EndNode struct {\n\tch       Chan\n\tchildren []Node\n}\n\nfunc (e *EndNode) Kind() op   { return EndOp }\nfunc (e *EndNode) Chan() Chan { return e.ch }\nfunc (e *EndNode) Append(n Node) Node {\n\te.children = append(e.children, n)\n\treturn n\n}\nfunc (e *EndNode) Child(i int) Node { return e.children[i] }\nfunc (e *EndNode) Children() []Node { return e.children }\nfunc (e *EndNode) String() string   { return fmt.Sprintf(\"End %s\", e.ch.Name()) }\n\ntype EmptyBodyNode struct {\n\tchildren []Node\n}\n\nfunc (e *EmptyBodyNode) Kind() op { return NoOp }\nfunc (e *EmptyBodyNode) Append(node Node) Node {\n\te.children = append(e.children, node)\n\treturn node\n}\nfunc (e *EmptyBodyNode) Child(i int) Node { return e.children[i] }\nfunc (e *EmptyBodyNode) Children() []Node { return e.children }\nfunc (e *EmptyBodyNode) String() string   { return \"(Empty)\" }\n\n// NewNewChanNode makes a NewChanNode.\nfunc NewNewChanNode(ch Chan) Node {\n\treturn &NewChanNode{ch: ch, children: []Node{}}\n}\n\n// NewSendNode makes a SendNode.\nfunc NewSendNode(sndr Role, dest Chan, typ types.Type) Node {\n\treturn &SendNode{\n\t\tsndr:     sndr,\n\t\tdest:     dest,\n\t\tnondet:   false,\n\t\tt:        typ,\n\t\tchildren: []Node{},\n\t}\n}\n\n// NewSelectSendNode makes a SendNode in a select (non-deterministic).\nfunc NewSelectSendNode(sndr Role, dest Chan, typ types.Type) Node {\n\treturn &SendNode{\n\t\tsndr:     sndr,\n\t\tdest:     dest,\n\t\tnondet:   true,\n\t\tt:        typ,\n\t\tchildren: []Node{},\n\t}\n}\n\n// NewRecvNode makes a RecvNode.\nfunc NewRecvNode(orig Chan, rcvr Role, typ types.Type) Node {\n\treturn &RecvNode{\n\t\torig:     orig,\n\t\trcvr:     rcvr,\n\t\tnondet:   false,\n\t\tt:        typ,\n\t\tstop:     false,\n\t\tchildren: []Node{},\n\t}\n}\n\n// NewRecvStopNode makes a RecvNode (for STOP messages).\nfunc NewRecvStopNode(orig Chan, rcvr Role, typ types.Type) Node {\n\treturn &RecvNode{\n\t\torig:     orig,\n\t\trcvr:     rcvr,\n\t\tnondet:   false,\n\t\tt:        typ,\n\t\tstop:     true,\n\t\tchildren: []Node{},\n\t}\n}\n\n// NewSelectRecvNode makes a RecvNode in a select (non-deterministic).\nfunc NewSelectRecvNode(orig Chan, rcvr Role, typ types.Type) Node {\n\treturn &RecvNode{\n\t\torig:     orig,\n\t\trcvr:     rcvr,\n\t\tnondet:   true,\n\t\tt:        typ,\n\t\tchildren: []Node{},\n\t}\n}\n\n// NewLabelNode makes a LabelNode.\nfunc NewLabelNode(name string) Node {\n\treturn &LabelNode{\n\t\tname:     name,\n\t\tchildren: []Node{},\n\t}\n}\n\n// NewGotoNode makes a GotoNode.\nfunc NewGotoNode(name string) Node {\n\treturn &GotoNode{\n\t\tname:     name,\n\t\tchildren: []Node{},\n\t}\n}\n\n// NewEndNode makse an EndNode.\nfunc NewEndNode(ch Chan) Node {\n\treturn &EndNode{\n\t\tch: ch,\n\t}\n}\n\n// String displays session details.\nfunc (s *Session) String() string {\n\tstr := \"# Channels\\n\"\n\tfor _, ch := range s.Chans {\n\t\tstr += fmt.Sprintf(\"%s \", ch.Name())\n\t}\n\tstr += \"\\n# Role\\n\"\n\tfor _, r := range s.Roles {\n\t\tstr += fmt.Sprintf(\"%s \", r.Name())\n\t}\n\tstr += \"\\n# Session\\n\"\n\tfor role, session := range s.Types {\n\t\tstr += fmt.Sprintf(\"  %s: %s\", role.Name(), StringRecursive(session))\n\t\tstr += \"\\n\"\n\t}\n\treturn str\n}\n\nfunc StringRecursive(node Node) string {\n\tstr := \"\"\n\tif node == nil {\n\t\treturn str\n\t}\n\n\tstr += node.String() + \"; \"\n\tswitch len(node.Children()) {\n\tcase 0:\n\tcase 1:\n\t\tstr += StringRecursive(node.Children()[0])\n\tdefault:\n\t\tstr += fmt.Sprintf(\"children: %d &{\", len(node.Children()))\n\t\tfor i, child := range node.Children() {\n\t\t\tif i > 0 {\n\t\t\t\tstr += \",\"\n\t\t\t}\n\t\t\tstr += StringRecursive(child)\n\t\t}\n\t\tstr += \"}\"\n\t}\n\treturn str\n}\n"
  },
  {
    "path": "cfsmextract/sesstype/sesstype_test.go",
    "content": "package sesstype\n\nimport (\n\t\"go/token\"\n\t\"go/types\"\n\t\"testing\"\n\n\t\"github.com/nickng/cfsm\"\n\t\"github.com/nickng/dingo-hunter/cfsmextract/utils\"\n\t\"golang.org/x/tools/go/ssa\"\n)\n\n// Tests SendNode creation.\nfunc TestSendNode(t *testing.T) {\n\ts := CreateSession()\n\tr := s.GetRole(\"main\")\n\tc := s.MakeChan(utils.NewDef(utils.EmptyValue{T: nil}), r)\n\tn := NewSendNode(r, c, nil)\n\tif n.Kind() != SendOp {\n\t\tt.Errorf(\"Expecting node kind to be %s but got %s\\n\", SendOp, n.Kind())\n\t}\n\tif n.(*SendNode).nondet {\n\t\tt.Errorf(\"Expecting Send to be deterministic by default\\n\")\n\t}\n\tif len(n.Children()) != 0 {\n\t\tt.Errorf(\"Expecting node to have 0 children but got %d\\n\", len(n.Children()))\n\t}\n\n\tn2 := NewSelectSendNode(r, c, nil)\n\tif n2.Kind() != SendOp {\n\t\tt.Errorf(\"Expecting node kind to be %s but got %s\\n\", SendOp, n2.Kind())\n\t}\n\tif !n2.(*SendNode).nondet {\n\t\tt.Errorf(\"Expecting Select-Send to be non-deterministic by default\\n\")\n\t}\n\tif len(n2.Children()) != 0 {\n\t\tt.Errorf(\"Expecting node to have 0 children but got %d\\n\", len(n2.Children()))\n\t}\n\n\tif n2 != n.Append(n2) {\n\t\tt.Errorf(\"Appended node is not same as expected\\n\")\n\t}\n\tif len(n.Children()) != 1 {\n\t\tt.Errorf(\"Expecting node to have 1 children but got %d\\n\", len(n.Children()))\n\t}\n\n}\n\n// Tests RecvNode creation.\nfunc TestRecvNode(t *testing.T) {\n\ts := CreateSession()\n\tr := s.GetRole(\"main\")\n\tc := s.MakeChan(utils.NewDef(utils.EmptyValue{T: nil}), r)\n\tn := NewRecvNode(c, r, nil)\n\tif n.Kind() != RecvOp {\n\t\tt.Errorf(\"Expecting node kind to be %s but got %s\\n\", RecvOp, n.Kind())\n\t}\n\tif n.(*RecvNode).nondet {\n\t\tt.Errorf(\"Expecting Receive to be deterministic by default\\n\")\n\t}\n\tif len(n.Children()) != 0 {\n\t\tt.Errorf(\"Expecting node to have 0 children but got %d\\n\", len(n.Children()))\n\t}\n\n\tn2 := NewSelectRecvNode(c, r, nil)\n\tif n2.Kind() != RecvOp {\n\t\tt.Errorf(\"Expecting node kind to be %s but got %s\\n\", RecvOp, n2.Kind())\n\t}\n\tif !n2.(*RecvNode).nondet {\n\t\tt.Errorf(\"Expecting Select-Recv to be non-deterministic by default\\n\")\n\t}\n\tif len(n2.Children()) != 0 {\n\t\tt.Errorf(\"Expecting node to have 0 children but got %d\\n\", len(n2.Children()))\n\t}\n\n\tif n2 != n.Append(n2) {\n\t\tt.Errorf(\"Appended node is not same as expected\\n\")\n\t}\n\tif len(n.Children()) != 1 {\n\t\tt.Errorf(\"Expecting node to have 1 children but got %d\\n\", len(n.Children()))\n\t}\n\n}\n\n// Tests LabelNode and GotoNode creation.\nfunc TestLabelGotoNode(t *testing.T) {\n\tl := NewLabelNode(\"Name\")\n\tif l.Kind() != NoOp {\n\t\tt.Errorf(\"Expecting Goto node kind to be %s but got %s\\n\", NoOp, l.Kind())\n\t}\n\tif len(l.Children()) != 0 {\n\t\tt.Errorf(\"Expecting Label node to have 0 children but got %d\\n\", len(l.Children()))\n\t}\n\n\tg := NewGotoNode(\"Name\")\n\tif g.Kind() != NoOp {\n\t\tt.Errorf(\"Expecting Goto node kind to be %s but got %s\\n\", NoOp, g.Kind())\n\t}\n\tif len(g.Children()) != 0 {\n\t\tt.Errorf(\"Expecting Goto node to have 0 children but got %d\\n\", len(g.Children()))\n\t}\n\n\tif g != l.Append(g) {\n\t\tt.Error(\"Appended node is not same as expected\\n\")\n\t}\n\tif len(l.Children()) != 1 {\n\t\tt.Errorf(\"Expecting Label node to have 1 children but got %d\\n\", len(l.Children()))\n\t}\n}\n\n// Tests NewChanNode creation.\nfunc TestNewChanNode(t *testing.T) {\n\ts := CreateSession()\n\tr := s.GetRole(\"main\")\n\tc := s.MakeChan(utils.NewDef(utils.EmptyValue{T: nil}), r)\n\tn := NewNewChanNode(c)\n\tif n.Kind() != NewChanOp {\n\t\tt.Errorf(\"Expecting node kind to be %s but got %s\\n\", NewChanOp, n.Kind())\n\t}\n\tif len(n.Children()) != 0 {\n\t\tt.Errorf(\"Expecting node to have 0 children but got %d\\n\", len(n.Children()))\n\t}\n\tn2 := NewNewChanNode(c)\n\tif n2 != n.Append(n2) {\n\t\tt.Errorf(\"Appended node is not same as expected\\n\")\n\t}\n\tif len(n.Children()) != 1 {\n\t\tt.Errorf(\"Expecting node to have 1 children but got %d\\n\", len(n.Children()))\n\t}\n}\n\n// Tests NewEndNode creation.\nfunc TestEndNode(t *testing.T) {\n\ts := CreateSession()\n\tr := s.GetRole(\"main\")\n\tc := s.MakeChan(utils.NewDef(utils.EmptyValue{T: nil}), r)\n\tn := NewEndNode(c)\n\tif n.Kind() != EndOp {\n\t\tt.Errorf(\"Expecting node kind to be %s but got %s\\n\", EndOp, n.Kind())\n\t}\n\tif len(n.Children()) != 0 {\n\t\tt.Errorf(\"Expecting node to have 0 children but got %d\\n\", len(n.Children()))\n\t}\n\tn2 := NewEndNode(c)\n\tif n2 != n.Append(n2) {\n\t\tt.Errorf(\"Appended node is not same as expected\\n\")\n\t}\n\tif len(n.Children()) != 1 {\n\t\tt.Errorf(\"Expecting node to have 1 children but got %d\\n\", len(n.Children()))\n\t}\n}\n\ntype mockChan struct{}\n\nfunc (mc mockChan) Name() string                  { return \"MockChan\" }\nfunc (mc mockChan) String() string                { return \"Mock Chan\" }\nfunc (mc mockChan) Type() types.Type              { return types.NewChan(types.SendRecv, types.NewStruct(nil, nil)) }\nfunc (mc mockChan) Parent() *ssa.Function         { return nil }\nfunc (mc mockChan) Referrers() *[]ssa.Instruction { return nil }\nfunc (mc mockChan) Pos() token.Pos                { return token.NoPos }\n\nfunc TestSelfLoop(t *testing.T) {\n\ts := CreateSession()\n\tr := s.GetRole(\"main\")\n\tc := s.MakeChan(utils.NewDef(mockChan{}), r)\n\n\tn0 := NewLabelNode(\"BeforeReceive\")\n\tn1 := NewRecvNode(c, r, types.NewStruct(nil, nil))\n\tn2 := NewGotoNode(\"BeforeReceive\")\n\tn0.Append(n1)\n\tn1.Append(n2)\n\n\tms := NewCFSMs(s)\n\tm := ms.Sys.NewMachine()\n\tms.States[m] = make(map[string]*cfsm.State)\n\tms.rootToMachine(r, n0, m)\n\tif want, got := 1, len(m.States()); want != got {\n\t\tt.Errorf(\"expecting %d states for self-loop but got %d\", want, got)\n\t}\n\tif want, got := 1, len(m.States()[0].Transitions()); want != got {\n\t\tt.Errorf(\"expecting %d transitions for self-loop but got %d\", want, got)\n\t}\n\tif from, to := m.States()[0], m.States()[0].Transitions()[0].State(); from != to {\n\t\tt.Errorf(\"expecting self-loop but got %s\", m.String())\n\t}\n}\n"
  },
  {
    "path": "cfsmextract/utils/defs.go",
    "content": "package utils\n\nimport (\n\t\"fmt\"\n\n\t\"golang.org/x/tools/go/ssa\"\n)\n\nvar (\n\tVarVers = make(map[ssa.Value]int)\n)\n\n// Variable definitions\ntype Definition struct {\n\tVar ssa.Value\n\tVer int\n}\n\n// NewVarDef creates a new variable definition from an ssa.Value\nfunc NewDef(v ssa.Value) *Definition {\n\tif v == nil {\n\t\tpanic(\"NewVarDef: Cannot create new VarDef with nil\")\n\t}\n\tif ver, ok := VarVers[v]; ok {\n\t\tVarVers[v]++\n\t\treturn &Definition{\n\t\t\tVar: v,\n\t\t\tVer: ver + 1,\n\t\t}\n\t}\n\tVarVers[v] = 0\n\treturn &Definition{\n\t\tVar: v,\n\t\tVer: 0,\n\t}\n}\n\nfunc (vd *Definition) String() string {\n\tif vd == nil || vd.Var == nil {\n\t\treturn \"Undefined\"\n\t}\n\tif vd.Var.Parent() != nil {\n\t\treturn fmt.Sprintf(\"%s.%s@%d\", vd.Var.Parent().String(), vd.Var.Name(), vd.Ver)\n\t}\n\treturn fmt.Sprintf(\"???.%s@%d\", vd.Var.Name(), vd.Ver)\n}\n"
  },
  {
    "path": "cfsmextract/utils/emptyvalue.go",
    "content": "package utils\n\nimport (\n\t\"go/token\"\n\t\"go/types\"\n\n\t\"golang.org/x/tools/go/ssa\"\n)\n\nvar (\n\t_ ssa.Value = (*EmptyValue)(nil) // Make sure it implements ssa.Value.\n)\n\n// EmptyValue is a ssa.Value placeholder for values we don't care.\ntype EmptyValue struct {\n\tT types.Type\n}\n\nfunc (v EmptyValue) Name() string                  { return \"(Nothingness)\" }\nfunc (v EmptyValue) String() string                { return \"(Empty Value)\" }\nfunc (v EmptyValue) Type() types.Type              { return v.T }\nfunc (v EmptyValue) Parent() *ssa.Function         { return nil }\nfunc (v EmptyValue) Referrers() *[]ssa.Instruction { return nil }\nfunc (v EmptyValue) Pos() token.Pos                { return token.NoPos }\n"
  },
  {
    "path": "cfsmextract/utils.go",
    "content": "package cfsmextract\n\n// From golang.org/x/tools/go/ssa/interp/interp.go\n\nimport (\n\t\"fmt\"\n\t\"go/token\"\n\t\"go/types\"\n\n\t\"golang.org/x/tools/go/ssa\"\n)\n\nfunc loc(fr *frame, pos token.Pos) string {\n\tif fr.fn == nil {\n\t\treturn \"(unknown)\"\n\t}\n\tif pos == token.NoPos {\n\t\treturn \"(unknown)\"\n\t}\n\treturn fr.fn.Prog.Fset.Position(pos).String()\n}\n\nfunc red(s string) string {\n\treturn fmt.Sprintf(\"\\033[31m%s\\033[0m\", s)\n}\n\nfunc orange(s string) string {\n\treturn fmt.Sprintf(\"\\033[33m%s\\033[0m\", s)\n}\n\nfunc green(s string) string {\n\treturn fmt.Sprintf(\"\\033[32m%s\\033[0m\", s)\n}\n\nfunc cyan(s string) string {\n\treturn fmt.Sprintf(\"\\033[36m%s\\033[0m\", s)\n}\n\nfunc reg(reg ssa.Value) string {\n\tif reg == nil {\n\t\treturn \"???.nil\"\n\t}\n\tif reg.Parent() != nil {\n\t\treturn fmt.Sprintf(\"%s.\\033[4m%s\\033[0m\", reg.Parent().String(), reg.Name())\n\t}\n\treturn fmt.Sprintf(\"???.\\033[4m%s\\033[0m\", reg.Name())\n}\n\nfunc deref(typ types.Type) types.Type {\n\tif p, ok := typ.Underlying().(*types.Pointer); ok {\n\t\treturn p.Elem()\n\t}\n\treturn typ\n}\n\nfunc derefAll(typ types.Type) types.Type {\n\tt := typ\n\tfor {\n\t\tif p, ok := t.Underlying().(*types.Pointer); ok {\n\t\t\tt = p.Elem()\n\t\t} else {\n\t\t\treturn t\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "cfsmextract/visit.go",
    "content": "package cfsmextract\n\nimport (\n\t\"fmt\"\n\t\"go/token\"\n\t\"go/types\"\n\t\"os\"\n\n\t\"github.com/nickng/dingo-hunter/cfsmextract/sesstype\"\n\t\"github.com/nickng/dingo-hunter/cfsmextract/utils\"\n\t\"golang.org/x/tools/go/ssa\"\n)\n\nfunc visitBlock(blk *ssa.BasicBlock, fr *frame) {\n\tif len(blk.Preds) > 1 {\n\t\tblkLabel := fmt.Sprintf(\"%s#%d\", blk.Parent().String(), blk.Index)\n\n\t\tif _, found := fr.gortn.visited[blk]; found {\n\t\t\tfr.gortn.AddNode(sesstype.NewGotoNode(blkLabel))\n\t\t\treturn\n\t\t}\n\t\t// Make a label for other edges that enter this block\n\t\tlabel := sesstype.NewLabelNode(blkLabel)\n\t\tfr.gortn.AddNode(label)\n\t\tfr.gortn.visited[blk] = label // XXX visited is initialised by append if lblNode is head of tree\n\t}\n\n\tfor _, inst := range blk.Instrs {\n\t\tvisitInst(inst, fr)\n\t}\n}\n\n// visitFunc is called to traverse a function using given callee frame\n// Returns a boolean representing whether or not there are code in the func.\nfunc visitFunc(fn *ssa.Function, callee *frame) bool {\n\tif fn.Blocks == nil {\n\t\t//fmt.Fprintf(os.Stderr, \"  # Ignore builtin/external '\"+fn.String()+\"' with no Blocks\\n\")\n\t\treturn false\n\t}\n\n\tvisitBlock(fn.Blocks[0], callee)\n\treturn true\n}\n\nfunc visitInst(inst ssa.Instruction, fr *frame) {\n\tswitch inst := inst.(type) {\n\tcase *ssa.MakeChan:\n\t\tvisitMakeChan(inst, fr)\n\n\tcase *ssa.Send:\n\t\tvisitSend(inst, fr)\n\n\tcase *ssa.UnOp:\n\t\tswitch inst.Op {\n\t\tcase token.ARROW:\n\t\t\tvisitRecv(inst, fr)\n\t\tcase token.MUL:\n\t\t\tvisitDeref(inst, fr)\n\t\tdefault:\n\t\t\tfmt.Fprintf(os.Stderr, \"   # unhandled %s = %s\\n\", red(inst.Name()), red(inst.String()))\n\t\t}\n\n\tcase *ssa.Call:\n\t\tvisitCall(inst, fr)\n\n\tcase *ssa.Extract:\n\t\tvisitExtract(inst, fr)\n\n\tcase *ssa.Go:\n\t\tfr.callGo(inst)\n\n\tcase *ssa.Return:\n\t\tfr.retvals = visitReturn(inst, fr)\n\n\tcase *ssa.Store:\n\t\tvisitStore(inst, fr)\n\n\tcase *ssa.Alloc:\n\t\tvisitAlloc(inst, fr)\n\n\tcase *ssa.MakeClosure:\n\t\tvisitMakeClosure(inst, fr)\n\n\tcase *ssa.Select:\n\t\tvisitSelect(inst, fr)\n\n\tcase *ssa.ChangeType:\n\t\tvisitChangeType(inst, fr)\n\n\tcase *ssa.ChangeInterface:\n\t\tvisitChangeInterface(inst, fr)\n\n\tcase *ssa.If:\n\t\tvisitIf(inst, fr)\n\n\tcase *ssa.Jump:\n\t\tvisitJump(inst, fr)\n\n\tcase *ssa.BinOp:\n\t\tvisitBinOp(inst, fr)\n\n\tcase *ssa.Slice:\n\t\tvisitSlice(inst, fr)\n\n\tcase *ssa.MakeSlice:\n\t\tvisitMakeSlice(inst, fr)\n\n\tcase *ssa.FieldAddr:\n\t\tvisitFieldAddr(inst, fr)\n\n\tcase *ssa.Field:\n\t\tvisitField(inst, fr)\n\n\tcase *ssa.IndexAddr:\n\t\tvisitIndexAddr(inst, fr)\n\n\tcase *ssa.Index:\n\t\tvisitIndex(inst, fr)\n\n\tcase *ssa.Defer:\n\t\tvisitDefer(inst, fr)\n\n\tcase *ssa.RunDefers:\n\t\tvisitRunDefers(inst, fr)\n\n\tcase *ssa.Phi:\n\t\tvisitPhi(inst, fr)\n\n\tcase *ssa.TypeAssert:\n\t\tvisitTypeAssert(inst, fr)\n\n\tcase *ssa.MakeInterface:\n\t\tvisitMakeInterface(inst, fr)\n\n\tcase *ssa.DebugRef:\n\n\tdefault:\n\t\t// Everything else not handled yet\n\t\tif v, ok := inst.(ssa.Value); ok {\n\t\t\tfmt.Fprintf(os.Stderr, \"   # unhandled %s = %s\\n\", red(v.Name()), red(v.String()))\n\t\t} else {\n\t\t\tfmt.Fprintf(os.Stderr, \"   # unhandled %s\\n\", red(inst.String()))\n\t\t}\n\t}\n}\n\nfunc visitExtract(e *ssa.Extract, fr *frame) {\n\tif recvCh, ok := fr.recvok[e.Tuple]; ok && e.Index == 1 { // 1 = ok (bool)\n\t\tfmt.Fprintf(os.Stderr, \"  EXTRACT for %s\\n\", recvCh.Name())\n\t\t//fr.locals[e] = e\n\t\tfr.env.recvTest[e] = recvCh\n\t\treturn\n\t}\n\tif tpl, ok := fr.tuples[e.Tuple]; ok {\n\t\tfmt.Fprintf(os.Stderr, \"   %s = extract %s[#%d] == %s\\n\", reg(e), e.Tuple.Name(), e.Index, tpl[e.Index].String())\n\t\tfr.locals[e] = tpl[e.Index]\n\t} else {\n\t\t// Check if we are extracting select index\n\t\tif _, ok := fr.env.selNode[e.Tuple]; ok && e.Index == 0 {\n\t\t\tfmt.Fprintf(os.Stderr, \"   | %s = select %s index\\n\", e.Name(), e.Tuple.Name())\n\t\t\tfr.env.selIdx[e] = e.Tuple\n\t\t\treturn\n\t\t}\n\t\t// Check if value is an external tuple (return value)\n\t\tif extType, isExtern := fr.env.extern[e.Tuple]; isExtern {\n\t\t\tif extTpl, isTuple := extType.(*types.Tuple); isTuple {\n\t\t\t\tif extTpl.Len() < e.Index {\n\t\t\t\t\tpanic(fmt.Sprintf(\"Extract: Cannot extract from tuple %s\\n\", e.Tuple.Name()))\n\t\t\t\t}\n\t\t\t\t// if extracted value is a chan create a new channel for it\n\t\t\t\tif _, ok := extTpl.At(e.Index).Type().(*types.Chan); ok {\n\t\t\t\t\tpanic(\"Extract: Undefined channel\")\n\t\t\t\t}\n\t\t\t}\n\t\t\tif e.Index < len(tpl) {\n\t\t\t\tfmt.Fprintf(os.Stderr, \"  extract %s[#%d] == %s\\n\", e.Tuple.Name(), e.Index, tpl[e.Index].String())\n\t\t\t} else {\n\t\t\t\tfmt.Fprintf(os.Stderr, \"  extract %s[#%d/%d]\\n\", e.Tuple.Name(), e.Index, len(tpl))\n\t\t\t}\n\t\t} else {\n\t\t\tfmt.Fprintf(os.Stderr, \"   # %s = %s of type %s\\n\", e.Name(), red(e.String()), e.Type().String())\n\t\t\tswitch derefAll(e.Type()).Underlying().(type) {\n\t\t\tcase *types.Array:\n\t\t\t\tvd := utils.NewDef(e)\n\t\t\t\tfr.locals[e] = vd\n\t\t\t\tfr.arrays[vd] = make(Elems)\n\t\t\t\tfmt.Fprintf(os.Stderr, \"     ^ local array (used as definition)\\n\")\n\t\t\tcase *types.Struct:\n\t\t\t\tvd := utils.NewDef(e)\n\t\t\t\tfr.locals[e] = vd\n\t\t\t\tfr.structs[vd] = make(Fields)\n\t\t\t\tfmt.Fprintf(os.Stderr, \"     ^ local struct (used as definition)\\n\")\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc visitMakeClosure(inst *ssa.MakeClosure, fr *frame) {\n\tfr.env.closures[inst] = make([]*utils.Definition, 0)\n\tfor _, binding := range inst.Bindings {\n\t\tfr.env.closures[inst] = append(fr.env.closures[inst], fr.locals[binding])\n\t}\n}\n\n// visitAlloc is for variable allocation (usually by 'new')\n// Everything allocated here are pointers\nfunc visitAlloc(inst *ssa.Alloc, fr *frame) {\n\tlocn := loc(fr, inst.Pos())\n\tallocType := inst.Type().(*types.Pointer).Elem()\n\tif allocType == nil {\n\t\tpanic(\"Alloc: Cannot Alloc for non-pointer type\")\n\t}\n\tvar val ssa.Value = inst\n\n\tswitch t := allocType.Underlying().(type) {\n\tcase *types.Array:\n\t\tvd := utils.NewDef(val)\n\t\tfr.locals[val] = vd\n\t\tif inst.Heap {\n\t\t\tfr.env.arrays[vd] = make(Elems)\n\t\t\tfmt.Fprintf(os.Stderr, \"   %s = Alloc (array@heap) of type %s (%d elems) at %s\\n\", cyan(reg(inst)), inst.Type().String(), t.Len(), locn)\n\t\t} else {\n\t\t\tfr.arrays[vd] = make(Elems)\n\t\t\tfmt.Fprintf(os.Stderr, \"   %s = Alloc (array@local) of type %s (%d elems) at %s\\n\", cyan(reg(inst)), inst.Type().String(), t.Len(), locn)\n\t\t}\n\n\tcase *types.Chan:\n\t\t// VD will be created in MakeChan so no need to allocate here.\n\t\tfmt.Fprintf(os.Stderr, \"   %s = Alloc (chan) of type %s at %s\\n\", cyan(reg(inst)), inst.Type().String(), locn)\n\n\tcase *types.Struct:\n\t\tvd := utils.NewDef(val)\n\t\tfr.locals[val] = vd\n\t\tif inst.Heap {\n\t\t\tfr.env.structs[vd] = make(Fields, t.NumFields())\n\t\t\tfmt.Fprintf(os.Stderr, \"   %s = Alloc (struct@heap) of type %s (%d fields) at %s\\n\", cyan(reg(inst)), inst.Type().String(), t.NumFields(), locn)\n\t\t} else {\n\t\t\tfr.structs[vd] = make(Fields, t.NumFields())\n\t\t\tfmt.Fprintf(os.Stderr, \"   %s = Alloc (struct@local) of type %s (%d fields) at %s\\n\", cyan(reg(inst)), inst.Type().String(), t.NumFields(), locn)\n\t\t}\n\n\tdefault:\n\t\tfmt.Fprintf(os.Stderr, \"   # %s = \"+red(\"Alloc %s\")+\" of type %s\\n\", inst.Name(), inst.String(), t.String())\n\t}\n}\n\nfunc visitDeref(inst *ssa.UnOp, fr *frame) {\n\tptr := inst.X\n\tval := inst\n\n\tif _, ok := ptr.(*ssa.Global); ok {\n\t\tfr.locals[ptr] = fr.env.globals[ptr]\n\t\tfmt.Fprintf(os.Stderr, \"   %s = *%s (global) of type %s\\n\", cyan(reg(val)), ptr.Name(), ptr.Type().String())\n\t\tfmt.Fprintf(os.Stderr, \"    ^ i.e. %s\\n\", fr.locals[ptr].String())\n\n\t\tswitch deref(fr.locals[ptr].Var.Type()).(type) {\n\t\tcase *types.Array, *types.Slice:\n\t\t\tif _, ok := fr.env.arrays[fr.env.globals[ptr]]; !ok {\n\t\t\t\tfr.env.arrays[fr.env.globals[ptr]] = make(Elems)\n\t\t\t}\n\n\t\tcase *types.Struct:\n\t\t\tif _, ok := fr.env.structs[fr.env.globals[ptr]]; !ok {\n\t\t\t\tfr.env.structs[fr.env.globals[ptr]] = make(Fields)\n\t\t\t}\n\t\t}\n\t}\n\n\tswitch vd, kind := fr.get(ptr); kind {\n\tcase Array, LocalArray:\n\t\tfr.locals[val] = vd\n\t\tfmt.Fprintf(os.Stderr, \"   %s = *%s (array)\\n\", cyan(reg(val)), ptr.Name())\n\n\tcase Struct, LocalStruct:\n\t\tfr.locals[val] = vd\n\t\tfmt.Fprintf(os.Stderr, \"   %s = *%s (struct)\\n\", cyan(reg(val)), ptr.Name())\n\n\tcase Chan:\n\t\tfr.locals[val] = vd\n\t\tfmt.Fprintf(os.Stderr, \"   %s = *%s (previously initalised Chan)\\n\", cyan(reg(val)), ptr.Name())\n\n\tcase Nothing:\n\t\tfmt.Fprintf(os.Stderr, \"   # %s = *%s (not found)\\n\", red(inst.String()), red(inst.X.String()))\n\t\tif _, ok := val.Type().Underlying().(*types.Chan); ok {\n\t\t\tfmt.Fprintf(os.Stderr, \"     ^ channel (not allocated, must be initialised by MakeChan)\")\n\t\t}\n\n\tdefault:\n\t\tfmt.Fprintf(os.Stderr, \"   # %s = *%s/%s (not found, type=%s)\\n\", red(inst.String()), red(inst.X.String()), reg(inst.X), inst.Type().String())\n\t}\n}\n\nfunc visitSelect(s *ssa.Select, fr *frame) {\n\tif fr.gortn.leaf == nil {\n\t\tpanic(\"Select: Session head Node cannot be nil\")\n\t}\n\n\tfr.env.selNode[s] = struct {\n\t\tparent   *sesstype.Node\n\t\tblocking bool\n\t}{\n\t\tfr.gortn.leaf,\n\t\ts.Blocking,\n\t}\n\tfor _, state := range s.States {\n\t\tlocn := loc(fr, state.Chan.Pos())\n\t\tswitch vd, kind := fr.get(state.Chan); kind {\n\t\tcase Chan:\n\t\t\tch := fr.env.chans[vd]\n\t\t\tfmt.Fprintf(os.Stderr, \"   select \"+orange(\"%s\")+\" (%d states)\\n\", vd.String(), len(s.States))\n\t\t\tswitch state.Dir {\n\t\t\tcase types.SendOnly:\n\t\t\t\tfr.gortn.leaf = fr.env.selNode[s].parent\n\t\t\t\tfr.gortn.AddNode(sesstype.NewSelectSendNode(fr.gortn.role, *ch, state.Chan.Type()))\n\t\t\t\tfmt.Fprintf(os.Stderr, \"    %s\\n\", orange((*fr.gortn.leaf).String()))\n\n\t\t\tcase types.RecvOnly:\n\t\t\t\tfr.gortn.leaf = fr.env.selNode[s].parent\n\t\t\t\tfr.gortn.AddNode(sesstype.NewSelectRecvNode(*ch, fr.gortn.role, state.Chan.Type()))\n\t\t\t\tfmt.Fprintf(os.Stderr, \"    %s\\n\", orange((*fr.gortn.leaf).String()))\n\n\t\t\tdefault:\n\t\t\t\tpanic(\"Select: Cannot handle with SendRecv channels\")\n\t\t\t}\n\n\t\tcase Nothing:\n\t\t\tfr.printCallStack()\n\t\t\tpanic(fmt.Sprintf(\"Select: Channel %s at %s is undefined\", reg(state.Chan), locn))\n\n\t\tdefault:\n\t\t\tfr.printCallStack()\n\t\t\tpanic(fmt.Sprintf(\"Select: Channel %s at %s is of wrong kind\", reg(state.Chan), locn))\n\t\t}\n\t}\n\tif !s.Blocking { // Default state exists\n\t\tfr.gortn.leaf = fr.env.selNode[s].parent\n\t\tfr.gortn.AddNode(&sesstype.EmptyBodyNode{})\n\t\tfmt.Fprintf(os.Stderr, \"    Default: %s\\n\", orange((*fr.gortn.leaf).String()))\n\t}\n}\n\nfunc visitReturn(ret *ssa.Return, fr *frame) []*utils.Definition {\n\tvar vds []*utils.Definition\n\tfor _, result := range ret.Results {\n\t\tvds = append(vds, fr.locals[result])\n\t}\n\treturn vds\n}\n\n// Handles function call.\n// Wrapper for calling visitFunc and performing argument translation.\nfunc visitCall(c *ssa.Call, caller *frame) {\n\tcaller.call(c)\n}\n\nfunc visitIf(inst *ssa.If, fr *frame) {\n\tif len(inst.Block().Succs) != 2 {\n\t\tpanic(\"If: Cannot handle If with more or less than 2 successor blocks!\")\n\t}\n\n\tifparent := fr.gortn.leaf\n\tif ifparent == nil {\n\t\tpanic(\"If: Parent is nil\")\n\t}\n\n\tif ch, isRecvTest := fr.env.recvTest[inst.Cond]; isRecvTest {\n\t\tfmt.Fprintf(os.Stderr, \"  @ Switch to recvtest true\\n\")\n\t\tfr.gortn.leaf = ifparent\n\t\tfr.gortn.AddNode(sesstype.NewRecvNode(*ch, fr.gortn.role, ch.Type()))\n\t\tfmt.Fprintf(os.Stderr, \"  %s\\n\", orange((*fr.gortn.leaf).String()))\n\t\tvisitBlock(inst.Block().Succs[0], fr)\n\n\t\tfmt.Fprintf(os.Stderr, \"  @ Switch to recvtest false\\n\")\n\t\tfr.gortn.leaf = ifparent\n\t\tfr.gortn.AddNode(sesstype.NewRecvStopNode(*ch, fr.gortn.role, ch.Type()))\n\t\tfmt.Fprintf(os.Stderr, \"  %s\\n\", orange((*fr.gortn.leaf).String()))\n\t\tvisitBlock(inst.Block().Succs[1], fr)\n\t} else if selTest, isSelTest := fr.env.selTest[inst.Cond]; isSelTest {\n\t\t// Check if this is a select-test-jump, if so handle separately.\n\t\tfmt.Fprintf(os.Stderr, \"  @ Switch to select branch #%d\\n\", selTest.idx)\n\t\tif selParent, ok := fr.env.selNode[selTest.tpl]; ok {\n\t\t\tfr.gortn.leaf = ifparent\n\t\t\t*fr.gortn.leaf = (*selParent.parent).Child(selTest.idx)\n\t\t\tvisitBlock(inst.Block().Succs[0], fr)\n\n\t\t\tif !selParent.blocking && len((*selParent.parent).Children()) > selTest.idx+1 {\n\t\t\t\t*fr.gortn.leaf = (*selParent.parent).Child(selTest.idx + 1)\n\t\t\t}\n\t\t\tvisitBlock(inst.Block().Succs[1], fr)\n\t\t} else {\n\t\t\tpanic(\"Select without corresponding sesstype.Node\")\n\t\t}\n\t} else {\n\t\tfr.env.ifparent.Push(*fr.gortn.leaf)\n\n\t\tparent := fr.env.ifparent.Top()\n\t\tfr.gortn.leaf = &parent\n\t\tfr.gortn.AddNode(&sesstype.EmptyBodyNode{})\n\t\tvisitBlock(inst.Block().Succs[0], fr)\n\n\t\tparent = fr.env.ifparent.Top()\n\t\tfr.gortn.leaf = &parent\n\t\tfr.gortn.AddNode(&sesstype.EmptyBodyNode{})\n\t\tvisitBlock(inst.Block().Succs[1], fr)\n\n\t\tfr.env.ifparent.Pop()\n\t}\n\t// This is end of the block so no continuation\n}\n\nfunc visitMakeChan(inst *ssa.MakeChan, caller *frame) {\n\tlocn := loc(caller, inst.Pos())\n\trole := caller.gortn.role\n\n\tvd := utils.NewDef(inst) // Unique identifier for inst\n\tch := caller.env.session.MakeChan(vd, role)\n\n\tcaller.env.chans[vd] = &ch\n\tcaller.gortn.AddNode(sesstype.NewNewChanNode(ch))\n\tcaller.locals[inst] = vd\n\tfmt.Fprintf(os.Stderr, \"   New channel %s { type: %s } by %s at %s\\n\", green(ch.Name()), ch.Type(), vd.String(), locn)\n\tfmt.Fprintf(os.Stderr, \"               ^ in role %s\\n\", role.Name())\n}\n\nfunc visitSend(send *ssa.Send, fr *frame) {\n\tlocn := loc(fr, send.Chan.Pos())\n\tif vd, kind := fr.get(send.Chan); kind == Chan {\n\t\tch := fr.env.chans[vd]\n\t\tfr.gortn.AddNode(sesstype.NewSendNode(fr.gortn.role, *ch, send.Chan.Type()))\n\t\tfmt.Fprintf(os.Stderr, \"  %s\\n\", orange((*fr.gortn.leaf).String()))\n\t} else if kind == Nothing {\n\t\tfr.locals[send.Chan] = utils.NewDef(send.Chan)\n\t\tch := fr.env.session.MakeExtChan(fr.locals[send.Chan], fr.gortn.role)\n\t\tfr.env.chans[fr.locals[send.Chan]] = &ch\n\t\tfr.gortn.AddNode(sesstype.NewSendNode(fr.gortn.role, ch, send.Chan.Type()))\n\t\tfmt.Fprintf(os.Stderr, \"  %s\\n\", orange((*fr.gortn.leaf).String()))\n\t\tfmt.Fprintf(os.Stderr, \"   ^ Send: Channel %s at %s is external\\n\", reg(send.Chan), locn)\n\t} else {\n\t\tfr.printCallStack()\n\t\tpanic(fmt.Sprintf(\"Send: Channel %s at %s is of wrong kind\", reg(send.Chan), locn))\n\t}\n}\n\nfunc visitRecv(recv *ssa.UnOp, fr *frame) {\n\tlocn := loc(fr, recv.X.Pos())\n\tif vd, kind := fr.get(recv.X); kind == Chan {\n\t\tch := fr.env.chans[vd]\n\t\tif recv.CommaOk {\n\t\t\t// ReceiveOK test\n\t\t\tfr.recvok[recv] = ch\n\t\t\t// TODO(nickng) technically this should do receive (both branches)\n\t\t} else {\n\t\t\t// Normal receive\n\t\t\tfr.gortn.AddNode(sesstype.NewRecvNode(*ch, fr.gortn.role, recv.X.Type()))\n\t\t\tfmt.Fprintf(os.Stderr, \"  %s\\n\", orange((*fr.gortn.leaf).String()))\n\t\t}\n\t} else if kind == Nothing {\n\t\tfr.locals[recv.X] = utils.NewDef(recv.X)\n\t\tch := fr.env.session.MakeExtChan(fr.locals[recv.X], fr.gortn.role)\n\t\tfr.env.chans[fr.locals[recv.X]] = &ch\n\t\tfr.gortn.AddNode(sesstype.NewRecvNode(ch, fr.gortn.role, recv.X.Type()))\n\t\tfmt.Fprintf(os.Stderr, \"  %s\\n\", orange((*fr.gortn.leaf).String()))\n\t\tfmt.Fprintf(os.Stderr, \"   ^ Recv: Channel %s at %s is external\\n\", reg(recv.X), locn)\n\t} else {\n\t\tfr.printCallStack()\n\t\tpanic(fmt.Sprintf(\"Recv: Channel %s at %s is of wrong kind\", reg(recv.X), locn))\n\t}\n}\n\n// visitClose for the close() builtin primitive.\nfunc visitClose(ch sesstype.Chan, fr *frame) {\n\tfr.gortn.AddNode(sesstype.NewEndNode(ch))\n}\n\nfunc visitJump(inst *ssa.Jump, fr *frame) {\n\t//fmt.Fprintf(os.Stderr, \" -jump-> Block %d\\n\", inst.Block().Succs[0].Index)\n\tif len(inst.Block().Succs) != 1 {\n\t\tpanic(\"Cannot Jump with multiple successors!\")\n\t}\n\tvisitBlock(inst.Block().Succs[0], fr)\n}\n\nfunc visitStore(inst *ssa.Store, fr *frame) {\n\tsource := inst.Val\n\tdstPtr := inst.Addr // from Alloc or field/elem access\n\n\tif _, ok := dstPtr.(*ssa.Global); ok {\n\t\tvdOld, _ := fr.env.globals[dstPtr]\n\t\tswitch vd, kind := fr.get(source); kind {\n\t\tcase Array:\n\t\t\tfr.env.globals[dstPtr] = vd\n\t\t\tfr.updateDefs(vdOld, vd)\n\t\t\tfmt.Fprintf(os.Stderr, \"   # store (global) *%s = %s of type %s\\n\", dstPtr.String(), source.Name(), source.Type().String())\n\n\t\tcase Struct:\n\t\t\tfr.env.globals[dstPtr] = vd\n\t\t\tfr.updateDefs(vdOld, vd)\n\t\t\tfmt.Fprintf(os.Stderr, \"   # store (global) *%s = %s of type %s\\n\", reg(dstPtr), reg(source), source.Type().String())\n\n\t\tdefault:\n\t\t\tfmt.Fprintf(os.Stderr, \"   # store (global) *%s = %s of type %s\\n\", red(reg(dstPtr)), reg(source), source.Type().String())\n\t\t}\n\t} else {\n\t\tvdOld, _ := fr.get(dstPtr)\n\t\tswitch vd, kind := fr.get(source); kind {\n\t\tcase Array:\n\t\t\t// Pre: fr.locals[source] points to vd\n\t\t\t// Pre: fr.locals[dstPtr] points to (empty/outdated) vdOld\n\t\t\t// Post: fr.locals[source] unchanged\n\t\t\t// Post: fr.locals[dstPtr] points to vd\n\t\t\tfr.locals[dstPtr] = vd   // was vdOld\n\t\t\tfr.updateDefs(vdOld, vd) // Update all references to vdOld to vd\n\t\t\tfmt.Fprintf(os.Stderr, \"   # store array *%s = %s of type %s\\n\", cyan(reg(dstPtr)), reg(source), source.Type().String())\n\n\t\tcase LocalArray:\n\t\t\tfr.locals[dstPtr] = vd\n\t\t\tfr.updateDefs(vdOld, vd)\n\t\t\tfmt.Fprintf(os.Stderr, \"   store larray *%s = %s of type %s\\n\", cyan(reg(dstPtr)), reg(source), source.Type().String())\n\n\t\tcase Chan:\n\t\t\tfr.locals[dstPtr] = vd\n\t\t\tfr.updateDefs(vdOld, vd)\n\t\t\tfmt.Fprintf(os.Stderr, \"   store chan *%s = %s of type %s\\n\", cyan(reg(dstPtr)), reg(source), source.Type().String())\n\n\t\tcase Struct:\n\t\t\tfr.locals[dstPtr] = vd\n\t\t\tfr.updateDefs(vdOld, vd)\n\t\t\tfmt.Fprintf(os.Stderr, \"   store struct *%s = %s of type %s\\n\", cyan(reg(dstPtr)), reg(source), source.Type().String())\n\n\t\tcase LocalStruct:\n\t\t\tfr.locals[dstPtr] = vd\n\t\t\tfr.updateDefs(vdOld, vd)\n\t\t\tfmt.Fprintf(os.Stderr, \"   store lstruct *%s = %s of type %s\\n\", cyan(reg(dstPtr)), reg(source), source.Type().String())\n\n\t\tcase Untracked:\n\t\t\tfr.locals[dstPtr] = vd\n\t\t\tfmt.Fprintf(os.Stderr, \"   store update *%s = %s of type %s\\n\", cyan(reg(dstPtr)), reg(source), source.Type().String())\n\n\t\tcase Nothing:\n\t\t\tfmt.Fprintf(os.Stderr, \"   # store *%s = %s of type %s\\n\", red(reg(dstPtr)), reg(source), source.Type().String())\n\n\t\tdefault:\n\t\t\tfr.locals[dstPtr] = vd\n\t\t\tfmt.Fprintf(os.Stderr, \"   store *%s = %s of type %s\\n\", cyan(reg(dstPtr)), reg(source), source.Type().String())\n\t\t}\n\n\t}\n}\n\nfunc visitChangeType(inst *ssa.ChangeType, fr *frame) {\n\tswitch vd, kind := fr.get(inst.X); kind {\n\tcase Chan:\n\t\tfr.locals[inst] = vd // ChangeType from <-chan and chan<-\n\t\tch := fr.env.chans[vd]\n\t\tfmt.Fprintf(os.Stderr, \"   & changetype from %s to %s (channel %s)\\n\", green(reg(inst.X)), reg(inst), ch.Name())\n\t\tfmt.Fprintf(os.Stderr, \"                      ^ origin\\n\")\n\n\tcase Nothing:\n\t\tfmt.Fprintf(os.Stderr, \"   # changetype %s = %s %s\\n\", inst.Name(), inst.X.Name(), inst.String())\n\t\tfmt.Fprintf(os.Stderr, \"          ^ unknown kind\\n\")\n\n\tdefault:\n\t\tfr.locals[inst] = vd\n\t\tfmt.Fprintf(os.Stderr, \"   # changetype %s = %s\\n\", red(inst.Name()), inst.String())\n\t}\n}\n\nfunc visitChangeInterface(inst *ssa.ChangeInterface, fr *frame) {\n\tfr.locals[inst] = fr.locals[inst.X]\n\tfmt.Fprintf(os.Stderr, \"   # changeinterface %s = %s\\n\", reg(inst), inst.String())\n}\n\nfunc visitBinOp(inst *ssa.BinOp, fr *frame) {\n\tswitch inst.Op {\n\tcase token.EQL:\n\t\tif selTuple, isSelTuple := fr.env.selIdx[inst.X]; isSelTuple {\n\t\t\tbranchID := int(inst.Y.(*ssa.Const).Int64())\n\t\t\tfr.env.selTest[inst] = struct {\n\t\t\t\tidx int\n\t\t\t\ttpl ssa.Value\n\t\t\t}{\n\t\t\t\tbranchID, selTuple,\n\t\t\t}\n\t\t} else {\n\t\t\tfmt.Fprintf(os.Stderr, \"   # %s = \"+red(\"%s\")+\"\\n\", inst.Name(), inst.String())\n\t\t}\n\tdefault:\n\t\tfmt.Fprintf(os.Stderr, \"   # %s = \"+red(\"%s\")+\"\\n\", inst.Name(), inst.String())\n\t}\n}\n\nfunc visitMakeInterface(inst *ssa.MakeInterface, fr *frame) {\n\tswitch vd, kind := fr.get(inst.X); kind {\n\tcase Struct, LocalStruct:\n\t\tfmt.Fprintf(os.Stderr, \"   %s <-(struct/iface)- %s %s = %s\\n\", cyan(reg(inst)), reg(inst.X), inst.String(), vd.String())\n\t\tfr.locals[inst] = vd\n\n\tcase Array, LocalArray:\n\t\tfmt.Fprintf(os.Stderr, \"   %s <-(array/iface)- %s %s = %s\\n\", cyan(reg(inst)), reg(inst.X), inst.String(), vd.String())\n\t\tfr.locals[inst] = vd\n\n\tdefault:\n\t\tfmt.Fprintf(os.Stderr, \"   # %s <- %s\\n\", red(reg(inst)), inst.String())\n\t}\n}\n\nfunc visitSlice(inst *ssa.Slice, fr *frame) {\n\tfr.env.arrays[utils.NewDef(inst)] = make(Elems)\n}\n\nfunc visitMakeSlice(inst *ssa.MakeSlice, fr *frame) {\n\tfr.env.arrays[utils.NewDef(inst)] = make(Elems)\n}\n\nfunc visitFieldAddr(inst *ssa.FieldAddr, fr *frame) {\n\tfield := inst\n\tstruc := inst.X\n\tindex := inst.Field\n\n\tif stype, ok := deref(struc.Type()).Underlying().(*types.Struct); ok {\n\t\tswitch vd, kind := fr.get(struc); kind {\n\t\tcase Struct:\n\t\t\tfmt.Fprintf(os.Stderr, \"   %s = %s(=%s)->[%d] of type %s\\n\", cyan(reg(field)), struc.Name(), vd.String(), index, field.Type().String())\n\t\t\tif fr.env.structs[vd][index] == nil { // First use\n\t\t\t\tvdField := utils.NewDef(field)\n\t\t\t\tfr.env.structs[vd][index] = vdField\n\t\t\t\tfmt.Fprintf(os.Stderr, \"     ^ accessed for the first time: use %s as field definition\\n\", field.Name())\n\t\t\t\t// If field is struct\n\t\t\t\tif fieldType, ok := deref(field.Type()).Underlying().(*types.Struct); ok {\n\t\t\t\t\tfr.env.structs[vdField] = make(Fields, fieldType.NumFields())\n\t\t\t\t\tfmt.Fprintf(os.Stderr, \"     ^ field %s is a struct (allocating)\\n\", field.Name())\n\t\t\t\t}\n\t\t\t} else if fr.env.structs[vd][index].Var != field { // Previously defined\n\t\t\t\tfmt.Fprintf(os.Stderr, \"     ^ field %s previously defined as %s\\n\", field.Name(), reg(fr.env.structs[vd][index].Var))\n\t\t\t} // else Accessed before (and unchanged)\n\t\t\tfr.locals[field] = fr.env.structs[vd][index]\n\n\t\tcase LocalStruct:\n\t\t\tfmt.Fprintf(os.Stderr, \"   %s = %s(=%s)->[%d] (local) of type %s\\n\", cyan(reg(field)), struc.Name(), vd.String(), index, field.Type().String())\n\t\t\tif fr.structs[vd][index] == nil { // First use\n\t\t\t\tvdField := utils.NewDef(field)\n\t\t\t\tfr.structs[vd][index] = vdField\n\t\t\t\tfmt.Fprintf(os.Stderr, \"     ^ accessed for the first time: use %s as field definition\\n\", field.Name())\n\t\t\t\t// If field is struct\n\t\t\t\tif fieldType, ok := deref(field.Type()).Underlying().(*types.Struct); ok {\n\t\t\t\t\tfr.structs[vdField] = make(Fields, fieldType.NumFields())\n\t\t\t\t\tfmt.Fprintf(os.Stderr, \"     ^ field %s is a struct (allocating locally)\\n\", field.Name())\n\t\t\t\t}\n\t\t\t} else if fr.structs[vd][index].Var != field { // Previously defined\n\t\t\t\tfmt.Fprintf(os.Stderr, \"     ^ field %s previously defined as %s\\n\", field.Name(), reg(fr.structs[vd][index].Var))\n\t\t\t} // else Accessed before (and unchanged)\n\t\t\tfr.locals[field] = fr.structs[vd][index]\n\n\t\tcase Nothing, Untracked:\n\t\t\t// Nothing: Very likely external struct.\n\t\t\t// Untracked: likely branches of return values (e.g. returning nil)\n\t\t\tfmt.Fprintf(os.Stderr, \"   %s = %s(=%s)->[%d] (external) of type %s\\n\", cyan(reg(field)), inst.X.Name(), vd.String(), index, field.Type().String())\n\t\t\tvd := utils.NewDef(struc) // New external struct\n\t\t\tfr.locals[struc] = vd\n\t\t\tfr.env.structs[vd] = make(Fields, stype.NumFields())\n\t\t\tvdField := utils.NewDef(field) // New external field\n\t\t\tfr.env.structs[vd][index] = vdField\n\t\t\tfr.locals[field] = vdField\n\t\t\tfmt.Fprintf(os.Stderr, \"     ^ accessed for the first time: use %s as field definition of type %s\\n\", field.Name(), inst.Type().(*types.Pointer).Elem().Underlying().String())\n\t\t\t// If field is struct\n\t\t\tif fieldType, ok := deref(field.Type()).Underlying().(*types.Struct); ok {\n\t\t\t\tfr.env.structs[vdField] = make(Fields, fieldType.NumFields())\n\t\t\t\tfmt.Fprintf(os.Stderr, \"     ^ field %s previously defined as %s\\n\", field.Name(), reg(fr.env.structs[vd][index].Var))\n\t\t\t}\n\n\t\tdefault:\n\t\t\tpanic(fmt.Sprintf(\"FieldAddr: Cannot access non-struct %s %T %d\", reg(struc), deref(struc.Type()).Underlying(), kind))\n\t\t}\n\t} else {\n\t\tpanic(fmt.Sprintf(\"FieldAddr: Cannot access field - %s not a struct\\n\", reg(struc)))\n\t}\n}\n\nfunc visitField(inst *ssa.Field, fr *frame) {\n\tfield := inst\n\tstruc := inst.X\n\tindex := inst.Field\n\n\tif stype, ok := struc.Type().Underlying().(*types.Struct); ok {\n\t\tswitch vd, kind := fr.get(struc); kind {\n\t\tcase Struct:\n\t\t\tfmt.Fprintf(os.Stderr, \"   %s = %s(=%s).[%d] of type %s\\n\", cyan(reg(field)), struc.Name(), vd.String(), index, field.Type().String())\n\t\t\tif fr.env.structs[vd][index] == nil { // First use\n\t\t\t\tvdField := utils.NewDef(field)\n\t\t\t\tfr.env.structs[vd][index] = vdField\n\t\t\t\tfmt.Fprintf(os.Stderr, \"     ^ accessed for the first time: use %s as field definition\\n\", field.Name())\n\t\t\t\t// If field is struct\n\t\t\t\tif fieldType, ok := field.Type().Underlying().(*types.Struct); ok {\n\t\t\t\t\tfr.env.structs[vdField] = make(Fields, fieldType.NumFields())\n\t\t\t\t\tfmt.Fprintf(os.Stderr, \"     ^ field %s is a struct (allocating)\\n\", field.Name())\n\t\t\t\t}\n\t\t\t} else if fr.env.structs[vd][index].Var != field { // Previously defined\n\t\t\t\tfmt.Fprintf(os.Stderr, \"     ^ field %s previously defined as %s\\n\", field.Name(), reg(fr.env.structs[vd][index].Var))\n\t\t\t} // else Accessed before (and unchanged)\n\t\t\tfr.locals[field] = fr.env.structs[vd][index]\n\n\t\tcase LocalStruct:\n\t\t\tfmt.Fprintf(os.Stderr, \"   %s = %s(=%s).[%d] (local) of type %s\\n\", cyan(reg(field)), struc.Name(), vd.String(), index, field.Type().String())\n\t\t\tif fr.structs[vd][index] == nil { // First use\n\t\t\t\tvdField := utils.NewDef(field)\n\t\t\t\tfr.structs[vd][index] = vdField\n\t\t\t\tfmt.Fprintf(os.Stderr, \"     ^ accessed for the first time: use %s as field definition\\n\", field.Name())\n\t\t\t\t// If field is struct\n\t\t\t\tif fieldType, ok := field.Type().Underlying().(*types.Struct); ok {\n\t\t\t\t\tfr.structs[vdField] = make(Fields, fieldType.NumFields())\n\t\t\t\t\tfmt.Fprintf(os.Stderr, \"     ^ field %s is a struct (allocating locally)\\n\", field.Name())\n\t\t\t\t}\n\t\t\t} else if fr.structs[vd][index].Var != field { // Previously defined\n\t\t\t\tfmt.Fprintf(os.Stderr, \"     ^ field %s previously defined as %s\\n\", field.Name(), reg(fr.structs[vd][index].Var))\n\t\t\t} // else Accessed before (and unchanged)\n\t\t\tfr.locals[field] = fr.structs[vd][index]\n\n\t\tcase Nothing, Untracked:\n\t\t\t// Nothing: Very likely external struct.\n\t\t\t// Untracked: likely branches of return values (e.g. returning nil)\n\t\t\tfmt.Fprintf(os.Stderr, \"   %s = %s(=%s).[%d] (external) of type %s\\n\", cyan(reg(field)), inst.X.Name(), vd.String(), index, field.Type().String())\n\t\t\tvd := utils.NewDef(struc) // New external struct\n\t\t\tfr.locals[struc] = vd\n\t\t\tfr.env.structs[vd] = make(Fields, stype.NumFields())\n\t\t\tvdField := utils.NewDef(field) // New external field\n\t\t\tfr.env.structs[vd][index] = vdField\n\t\t\tfr.locals[field] = vdField\n\t\t\tfmt.Fprintf(os.Stderr, \"     ^ accessed for the first time: use %s as field definition of type %s\\n\", field.Name(), inst.Type().Underlying().String())\n\t\t\t// If field is struct\n\t\t\tif fieldType, ok := field.Type().Underlying().(*types.Struct); ok {\n\t\t\t\tfr.env.structs[vdField] = make(Fields, fieldType.NumFields())\n\t\t\t\tfmt.Fprintf(os.Stderr, \"     ^ field %s previously defined as %s\\n\", field.Name(), reg(fr.env.structs[vd][index].Var))\n\t\t\t}\n\n\t\tdefault:\n\t\t\tpanic(fmt.Sprintf(\"Field: Cannot access non-struct %s %T %d\", reg(struc), struc.Type(), kind))\n\t\t}\n\t} else {\n\t\tpanic(fmt.Sprintf(\"Field: Cannot access field - %s not a struct\\n\", reg(struc)))\n\t}\n}\n\nfunc visitIndexAddr(inst *ssa.IndexAddr, fr *frame) {\n\telem := inst\n\tarray := inst.X\n\tindex := inst.Index\n\t_, isArray := deref(array.Type()).Underlying().(*types.Array)\n\t_, isSlice := deref(array.Type()).Underlying().(*types.Slice)\n\n\tif isArray || isSlice {\n\t\tswitch vd, kind := fr.get(array); kind {\n\t\tcase Array:\n\t\t\tfmt.Fprintf(os.Stderr, \"   %s = &%s(=%s)[%d] of type %s\\n\", cyan(reg(elem)), array.Name(), vd.String(), index, elem.Type().String())\n\t\t\tif fr.env.arrays[vd][index] == nil { // First use\n\t\t\t\tvdelem := utils.NewDef(elem)\n\t\t\t\tfr.env.arrays[vd][index] = vdelem\n\t\t\t\tfmt.Fprintf(os.Stderr, \"     ^ accessed for the first time: use %s as elem definition\\n\", elem.Name())\n\t\t\t} else if fr.env.arrays[vd][index].Var != elem { // Previously defined\n\t\t\t\tfmt.Fprintf(os.Stderr, \"     ^ elem %s previously defined as %s\\n\", elem.Name(), reg(fr.env.arrays[vd][index].Var))\n\t\t\t} // else Accessed before (and unchanged)\n\t\t\tfr.locals[elem] = fr.env.arrays[vd][index]\n\n\t\tcase LocalArray:\n\t\t\tfmt.Fprintf(os.Stderr, \"   %s = &%s(=%s)[%d] (local) of type %s\\n\", cyan(reg(elem)), array.Name(), vd.String(), index, elem.Type().String())\n\t\t\tif fr.arrays[vd][index] == nil { // First use\n\t\t\t\tvdElem := utils.NewDef(elem)\n\t\t\t\tfr.arrays[vd][index] = vdElem\n\t\t\t\tfmt.Fprintf(os.Stderr, \"     ^ accessed for the first time: use %s as elem definition\\n\", elem.Name())\n\t\t\t} else if fr.arrays[vd][index].Var != elem { // Previously defined\n\t\t\t\tfmt.Fprintf(os.Stderr, \"     ^ elem %s previously defined as %s\\n\", elem.Name(), reg(fr.arrays[vd][index].Var))\n\t\t\t} // else Accessed before (and unchanged)\n\t\t\tfr.locals[elem] = fr.arrays[vd][index]\n\n\t\tcase Nothing, Untracked:\n\t\t\t// Nothing: Very likely external struct.\n\t\t\t// Untracked: likely branches of return values (e.g. returning nil)\n\t\t\tfmt.Fprintf(os.Stderr, \"   %s = &%s(=%s)[%d] (external) of type %s\\n\", cyan(reg(elem)), inst.X.Name(), vd.String(), index, elem.Type().String())\n\t\t\tvd := utils.NewDef(array) // New external array\n\t\t\tfr.locals[array] = vd\n\t\t\tfr.env.arrays[vd] = make(Elems)\n\t\t\tvdElem := utils.NewDef(elem) // New external elem\n\t\t\tfr.env.arrays[vd][index] = vdElem\n\t\t\tfr.locals[elem] = vdElem\n\t\t\tfmt.Fprintf(os.Stderr, \"     ^ accessed for the first time: use %s as elem definition of type %s\\n\", elem.Name(), inst.Type().(*types.Pointer).Elem().Underlying().String())\n\n\t\tdefault:\n\t\t\tpanic(fmt.Sprintf(\"IndexAddr: Cannot access non-array %s\", reg(array)))\n\t\t}\n\t} else {\n\t\tpanic(fmt.Sprintf(\"IndexAddr: Cannot access field - %s not an array\", reg(array)))\n\t}\n}\n\nfunc visitIndex(inst *ssa.Index, fr *frame) {\n\telem := inst\n\tarray := inst.X\n\tindex := inst.Index\n\t_, isArray := array.Type().Underlying().(*types.Array)\n\t_, isSlice := array.Type().Underlying().(*types.Slice)\n\n\tif isArray || isSlice {\n\t\tswitch vd, kind := fr.get(array); kind {\n\t\tcase Array:\n\t\t\tfmt.Fprintf(os.Stderr, \"   %s = %s(=%s)[%d] of type %s\\n\", cyan(reg(elem)), array.Name(), vd.String(), index, elem.Type().String())\n\t\t\tif fr.env.arrays[vd][index] == nil { // First use\n\t\t\t\tvdelem := utils.NewDef(elem)\n\t\t\t\tfr.env.arrays[vd][index] = vdelem\n\t\t\t\tfmt.Fprintf(os.Stderr, \"     ^ accessed for the first time: use %s as elem definition\\n\", elem.Name())\n\t\t\t} else if fr.env.arrays[vd][index].Var != elem { // Previously defined\n\t\t\t\tfmt.Fprintf(os.Stderr, \"     ^ elem %s previously defined as %s\\n\", elem.Name(), reg(fr.env.arrays[vd][index].Var))\n\t\t\t} // else Accessed before (and unchanged)\n\t\t\tfr.locals[elem] = fr.env.arrays[vd][index]\n\n\t\tcase LocalArray:\n\t\t\tfmt.Fprintf(os.Stderr, \"   %s = %s(=%s)[%d] (local) of type %s\\n\", cyan(reg(elem)), array.Name(), vd.String(), index, elem.Type().String())\n\t\t\tif fr.arrays[vd][index] == nil { // First use\n\t\t\t\tvdElem := utils.NewDef(elem)\n\t\t\t\tfr.arrays[vd][index] = vdElem\n\t\t\t\tfmt.Fprintf(os.Stderr, \"     ^ accessed for the first time: use %s as elem definition\\n\", elem.Name())\n\t\t\t} else if fr.arrays[vd][index].Var != elem { // Previously defined\n\t\t\t\tfmt.Fprintf(os.Stderr, \"     ^ elem %s previously defined as %s\\n\", elem.Name(), reg(fr.arrays[vd][index].Var))\n\t\t\t} // else Accessed before (and unchanged)\n\t\t\tfr.locals[elem] = fr.arrays[vd][index]\n\n\t\tcase Nothing, Untracked:\n\t\t\t// Nothing: Very likely external struct.\n\t\t\t// Untracked: likely branches of return values (e.g. returning nil)\n\t\t\tfmt.Fprintf(os.Stderr, \"   %s = %s(=%s)[%d] (external) of type %s\\n\", cyan(reg(elem)), inst.X.Name(), vd.String(), index, elem.Type().String())\n\t\t\tvd := utils.NewDef(array) // New external array\n\t\t\tfr.locals[array] = vd\n\t\t\tfr.env.arrays[vd] = make(Elems)\n\t\t\tvdElem := utils.NewDef(elem) // New external elem\n\t\t\tfr.env.arrays[vd][index] = vdElem\n\t\t\tfr.locals[elem] = vdElem\n\t\t\tfmt.Fprintf(os.Stderr, \"     ^ accessed for the first time: use %s as elem definition of type %s\\n\", elem.Name(), inst.Type().(*types.Pointer).Elem().Underlying().String())\n\n\t\tdefault:\n\t\t\tpanic(fmt.Sprintf(\"Index: Cannot access non-array %s\", reg(array)))\n\t\t}\n\t} else {\n\t\tpanic(fmt.Sprintf(\"Index: Cannot access element - %s not an array\", reg(array)))\n\t}\n}\n\nfunc visitDefer(inst *ssa.Defer, fr *frame) {\n\tfr.defers = append(fr.defers, inst)\n}\n\nfunc visitRunDefers(inst *ssa.RunDefers, fr *frame) {\n\tfor i := len(fr.defers) - 1; i >= 0; i-- {\n\t\tfr.callCommon(fr.defers[i].Value(), fr.defers[i].Common())\n\t}\n}\n\nfunc visitPhi(inst *ssa.Phi, fr *frame) {\n\t// In the case of channels, find the last defined channel and replace it.\n\tif _, ok := inst.Type().(*types.Chan); ok {\n\t\t//preds := inst.Block().Preds // PredBlocks: order is significant.\n\t\tfr.locals[inst], _ = fr.get(inst.Edges[0])\n\t\tfr.phi[inst] = inst.Edges\n\t}\n}\n\nfunc visitTypeAssert(inst *ssa.TypeAssert, fr *frame) {\n\tif iface, ok := inst.AssertedType.(*types.Interface); ok {\n\t\tif meth, _ := types.MissingMethod(inst.X.Type(), iface, true); meth == nil { // No missing methods\n\t\t\tswitch vd, kind := fr.get(inst.X); kind {\n\t\t\tcase Struct, LocalStruct, Array, LocalArray, Chan:\n\t\t\t\tfr.tuples[inst] = make(Tuples, 2)\n\t\t\t\tfr.tuples[inst][0] = vd\n\t\t\t\tfmt.Fprintf(os.Stderr, \"   %s = %s.(type assert %s) iface\\n\", reg(inst), reg(inst.X), inst.AssertedType.String())\n\t\t\t\tfmt.Fprintf(os.Stderr, \"    ^ defined as %s\\n\", vd.String())\n\n\t\t\tdefault:\n\t\t\t\tfmt.Fprintf(os.Stderr, \"   %s = %s.(type assert %s)\\n\", red(reg(inst)), reg(inst.X), inst.AssertedType.String())\n\t\t\t\tfmt.Fprintf(os.Stderr, \"    ^ untracked/unknown\\n\")\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t} else { // Concrete type\n\t\tif types.Identical(inst.AssertedType.Underlying(), inst.X.Type().Underlying()) {\n\t\t\tswitch vd, kind := fr.get(inst.X); kind {\n\t\t\tcase Struct, LocalStruct, Array, LocalArray, Chan:\n\t\t\t\tfr.tuples[inst] = make(Tuples, 2)\n\t\t\t\tfr.tuples[inst][0] = vd\n\t\t\t\tfmt.Fprintf(os.Stderr, \"   %s = %s.(type assert %s) concrete\\n\", reg(inst), reg(inst.X), inst.AssertedType.String())\n\t\t\t\tfmt.Fprintf(os.Stderr, \"    ^ defined as %s\\n\", vd.String())\n\n\t\t\tdefault:\n\t\t\t\tfmt.Fprintf(os.Stderr, \"   %s = %s.(type assert %s)\\n\", red(reg(inst)), reg(inst.X), inst.AssertedType.String())\n\t\t\t\tfmt.Fprintf(os.Stderr, \"    ^ untracked/unknown\\n\")\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t}\n\tfmt.Fprintf(os.Stderr, \"   # %s = %s.(%s) impossible type assertion\\n\", red(reg(inst)), reg(inst.X), inst.AssertedType.String())\n}\n"
  },
  {
    "path": "cmd/buildssa.go",
    "content": "// Copyright © 2016 Nicholas Ng <nickng@projectfate.org>\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage cmd\n\nimport (\n\t\"log\"\n\t\"os\"\n\n\t\"github.com/nickng/dingo-hunter/logwriter\"\n\t\"github.com/nickng/dingo-hunter/ssabuilder\"\n\t\"github.com/spf13/cobra\"\n)\n\n// buildssaCmd represents the buildssa command\nvar buildssaCmd = &cobra.Command{\n\tUse:   \"buildssa\",\n\tShort: \"Build SSA IR of the input source files\",\n\tLong:  `Build SSA IR of the input source files`,\n\tRun: func(cmd *cobra.Command, args []string) {\n\t\tBuild(args)\n\t},\n}\n\nvar (\n\tdumpSSA bool\n\tdumpAll bool\n)\n\nfunc init() {\n\tRootCmd.AddCommand(buildssaCmd)\n\n\tbuildssaCmd.Flags().BoolVar(&dumpSSA, \"dump\", false, \"dump SSA IR of input files (based on CFG)\")\n\tbuildssaCmd.Flags().BoolVar(&dumpAll, \"dump-all\", false, \"dump all SSA IR of input files (including unused)\")\n\tif dumpSSA && dumpAll {\n\t\tdumpSSA = false // dumpAll override dumpSSA\n\t}\n}\n\nfunc Build(files []string) {\n\tlogFile, err := RootCmd.PersistentFlags().GetString(\"log\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tnoLogging, err := RootCmd.PersistentFlags().GetBool(\"no-logging\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tnoColour, err := RootCmd.PersistentFlags().GetBool(\"no-colour\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tl := logwriter.NewFile(logFile, !noLogging, !noColour)\n\tif err := l.Create(); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer l.Cleanup()\n\n\tconf, err := ssabuilder.NewConfig(files)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tconf.BuildLog = l.Writer\n\tssainfo, err := conf.Build()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tif dumpSSA {\n\t\tif _, err := ssainfo.WriteTo(os.Stdout); err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\t}\n\tif dumpAll {\n\t\tif _, err := ssainfo.WriteAll(os.Stdout); err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "cmd/cfsms.go",
    "content": "// Copyright © 2016 Nicholas Ng <nickng@projectfate.org>\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage cmd\n\nimport (\n\t\"log\"\n\n\t\"github.com/nickng/dingo-hunter/cfsmextract\"\n\t\"github.com/nickng/dingo-hunter/logwriter\"\n\t\"github.com/nickng/dingo-hunter/ssabuilder\"\n\t\"github.com/spf13/cobra\"\n)\n\nvar (\n\tprefix string // Output files prefix\n\toutdir string // CFMSs output directory\n)\n\n// cfsmsCmd represents the analyse command\nvar cfsmsCmd = &cobra.Command{\n\tUse:   \"cfsms\",\n\tShort: \"Extract CFSMs from source code\",\n\tLong: `Extract CFSMs from source code\n\nThe inputs should be a list of .go files in the same directory (of package main)\nOne of the .go file should contain the main function.`,\n\tRun: func(cmd *cobra.Command, args []string) {\n\t\textractCFSMs(args)\n\t},\n}\n\nfunc init() {\n\tcfsmsCmd.Flags().StringVar(&prefix, \"prefix\", \"output\", \"Output files prefix\")\n\tcfsmsCmd.Flags().StringVar(&outdir, \"outdir\", \"third_party/gmc-synthesis/inputs\", \"Output directory for CFSMs\")\n\n\tRootCmd.AddCommand(cfsmsCmd)\n}\n\nfunc extractCFSMs(files []string) {\n\tlogFile, err := RootCmd.PersistentFlags().GetString(\"log\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tnoLogging, err := RootCmd.PersistentFlags().GetBool(\"no-logging\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tnoColour, err := RootCmd.PersistentFlags().GetBool(\"no-colour\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tl := logwriter.NewFile(logFile, !noLogging, !noColour)\n\tif err := l.Create(); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer l.Cleanup()\n\n\tconf, err := ssabuilder.NewConfig(files)\n\tconf.BuildLog = l.Writer\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tssainfo, err := conf.Build()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\textract := cfsmextract.New(ssainfo, prefix, outdir)\n\tgo extract.Run()\n\n\tselect {\n\tcase <-extract.Error:\n\t\tlog.Fatal(err)\n\tcase <-extract.Done:\n\t\tlog.Println(\"Analysis finished in\", extract.Time)\n\t\textract.WriteOutput()\n\t}\n}\n"
  },
  {
    "path": "cmd/checkfair.go",
    "content": "// Copyright © 2016 Nicholas Ng <nickng@projectfate.org>\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage cmd\n\nimport (\n\t\"log\"\n\n\t\"github.com/nickng/dingo-hunter/fairness\"\n\t\"github.com/nickng/dingo-hunter/logwriter\"\n\t\"github.com/nickng/dingo-hunter/ssabuilder\"\n\t\"github.com/spf13/cobra\"\n)\n\n// checkfairCmd represents the check-fair command\nvar checkfairCmd = &cobra.Command{\n\tUse:   \"checkfair\",\n\tShort: \"Runs loop fairness checks\",\n\tLong: `Runs loop fairness checks\n\nThe checks will find potential problematic loops, such as those which are\nunbalanced - loop conditions favour infinite iterations deterministically`,\n\tRun: func(cmd *cobra.Command, args []string) {\n\t\tcheck(args)\n\t},\n}\n\nfunc init() {\n\tRootCmd.AddCommand(checkfairCmd)\n}\n\nfunc check(files []string) {\n\tlogFile, err := RootCmd.PersistentFlags().GetString(\"log\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tnoLogging, err := RootCmd.PersistentFlags().GetBool(\"no-logging\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tnoColour, err := RootCmd.PersistentFlags().GetBool(\"no-colour\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tl := logwriter.NewFile(logFile, !noLogging, !noColour)\n\tif err := l.Create(); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer l.Cleanup()\n\n\tconf, err := ssabuilder.NewConfig(files)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tconf.BuildLog = l.Writer\n\tssainfo, err := conf.Build()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tfairness.Check(ssainfo)\n}\n"
  },
  {
    "path": "cmd/migo.go",
    "content": "// Copyright © 2016 Nicholas Ng <nickng@projectfate.org>\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage cmd\n\nimport (\n\t\"log\"\n\t\"os\"\n\n\t\"github.com/nickng/dingo-hunter/logwriter\"\n\t\"github.com/nickng/dingo-hunter/migoextract\"\n\t\"github.com/nickng/dingo-hunter/ssabuilder\"\n\t\"github.com/nickng/migo/v3/migoutil\"\n\t\"github.com/spf13/cobra\"\n)\n\nvar (\n\toutfile string // Path to output file\n)\n\n// migoCmd represents the analyse command\nvar migoCmd = &cobra.Command{\n\tUse:   \"migo\",\n\tShort: \"Extract MiGo types from source code\",\n\tLong: `Extract MiGo types from source code\n\nThe inputs should be a list of .go files in the same directory (of package main)\nOne of the .go file should contain the main function.`,\n\tRun: func(cmd *cobra.Command, args []string) {\n\t\textractMigo(args)\n\t},\n}\n\nfunc init() {\n\tmigoCmd.Flags().StringVar(&outfile, \"output\", \"\", \"output migo file\")\n\n\tRootCmd.AddCommand(migoCmd)\n}\n\nfunc extractMigo(files []string) {\n\tlogFile, err := RootCmd.PersistentFlags().GetString(\"log\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tnoLogging, err := RootCmd.PersistentFlags().GetBool(\"no-logging\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tnoColour, err := RootCmd.PersistentFlags().GetBool(\"no-colour\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tl := logwriter.NewFile(logFile, !noLogging, !noColour)\n\tif err := l.Create(); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer l.Cleanup()\n\n\tconf, err := ssabuilder.NewConfig(files)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tconf.BuildLog = l.Writer\n\tssainfo, err := conf.Build()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\textract, err := migoextract.New(ssainfo, l.Writer)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tgo extract.Run()\n\n\tselect {\n\tcase <-extract.Error:\n\t\tlog.Fatal(err)\n\tcase <-extract.Done:\n\t\textract.Logger.Println(\"Analysis finished in\", extract.Time)\n\t}\n\n\tmigoutil.SimplifyProgram(extract.Env.MigoProg)\n\tif outfile != \"\" {\n\t\tf, err := os.Create(outfile)\n\t\tif err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\t\tf.WriteString(extract.Env.MigoProg.String())\n\t\tdefer f.Close()\n\t} else {\n\t\tos.Stdout.WriteString(extract.Env.MigoProg.String())\n\t}\n}\n"
  },
  {
    "path": "cmd/root.go",
    "content": "// Copyright © 2016 Nicholas Ng <nickng@projectfate.org>\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage cmd\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/spf13/cobra\"\n\t\"github.com/spf13/viper\"\n)\n\nvar (\n\tcfgFile   string // Path to config file\n\tlogFile   string // Path to log file\n\tnoLogging bool   // Turn off logging\n\tnoColour  bool   // Turn of colour output\n)\n\n// RootCmd represents the base command when called without any subcommands\nvar RootCmd = &cobra.Command{\n\tUse:   \"dingo-hunter\",\n\tShort: \"Static analyser for deadlock detection\",\n\tLong: `dingo-hunter is a static deadlock detector for Go\n\nThis is the toplevel command.\nUse \"dingo-hunter [command] sources.go...\" to analyse source files`,\n}\n\n// Execute adds all child commands to the root command sets flags appropriately.\n// This is called by main.main(). It only needs to happen once to the rootCmd.\nfunc Execute() {\n\tif err := RootCmd.Execute(); err != nil {\n\t\tfmt.Println(err)\n\t\tos.Exit(-1)\n\t}\n}\n\nfunc init() {\n\tcobra.OnInitialize(initConfig)\n\n\tRootCmd.PersistentFlags().StringVar(&cfgFile, \"config\", \"\", \"config file (default is $HOME/.dingo-hunter.yaml)\")\n\tRootCmd.PersistentFlags().StringVar(&logFile, \"log\", \"\", \"path to log file (default is stdout)\")\n\tRootCmd.PersistentFlags().BoolVar(&noLogging, \"no-logging\", false, \"disable logging\")\n\tRootCmd.PersistentFlags().BoolVar(&noColour, \"no-colour\", false, \"disable colour output\")\n}\n\n// initConfig reads in config file and ENV variables if set.\nfunc initConfig() {\n\tif cfgFile != \"\" { // enable ability to specify config file via flag\n\t\tviper.SetConfigFile(cfgFile)\n\t}\n\n\tviper.SetConfigName(\".dingo-hunter\") // name of config file (without extension)\n\tviper.AddConfigPath(\"$HOME\")         // adding home directory as first search path\n\tviper.AutomaticEnv()                 // read in environment variables that match\n\n\t// If a config file is found, read it in.\n\tif err := viper.ReadInConfig(); err == nil {\n\t\tfmt.Println(\"Using config file:\", viper.ConfigFileUsed())\n\t}\n}\n"
  },
  {
    "path": "cmd/serve.go",
    "content": "// Copyright © 2016 Nicholas Ng <nickng@projectfate.org>\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage cmd\n\nimport (\n\t\"go/build\"\n\t\"log\"\n\t\"path\"\n\n\t\"github.com/nickng/dingo-hunter/webservice\"\n\t\"github.com/spf13/cobra\"\n)\n\n// serveCmd represents the serve command\nvar serveCmd = &cobra.Command{\n\tUse:     \"serve\",\n\tAliases: []string{\"server\"},\n\tShort:   \"Run an HTTP webservice for demo\",\n\tLong: `Run an HTTP webservice for analysis demo.\n\nThe analysis will be presented side-by-side with its source code.\nEach example is a Go command (i.e. package main) in a directory, under the examples directory.`,\n\tRun: func(cmd *cobra.Command, args []string) {\n\t\tServe()\n\t},\n}\n\nconst basePkg = \"github.com/nickng/dingo-hunter\"\n\nvar (\n\taddr string // Listen interface.\n\tport string // Listen port.\n)\n\nfunc init() {\n\tRootCmd.AddCommand(serveCmd)\n\n\tp, err := build.Default.Import(basePkg, \"\", build.FindOnly)\n\tif err != nil {\n\t\tlog.Fatal(\"Could not find base path\")\n\t}\n\tbasePath := p.Dir\n\n\tserveCmd.Flags().StringVar(&addr, \"bind\", \"127.0.0.1\", \"Bind address. Defaults to 127.0.0.1.\")\n\tserveCmd.Flags().StringVar(&port, \"port\", \"6060\", \"Listen port. Defaults to 6060.\")\n\tserveCmd.Flags().StringVar(&webservice.ExamplesDir, \"examples\", path.Join(basePath, \"examples\", \"popl17\"), \"Path to examples directory\")\n\tserveCmd.Flags().StringVar(&webservice.TemplateDir, \"templates\", path.Join(basePath, \"templates\"), \"Path to templates directory\")\n\tserveCmd.Flags().StringVar(&webservice.StaticDir, \"static\", path.Join(basePath, \"static\"), \"Path to static files directory\")\n}\n\n// Serve starts the HTTP server.\nfunc Serve() {\n\tserver := webservice.NewServer(addr, port)\n\tserver.Start()\n\tserver.Close()\n}\n"
  },
  {
    "path": "doc.go",
    "content": "// Command dingo-hunter is a tool for analysing Go code to extract the\n// communication patterns for deadlock analysis.\n//\n// The deadlock analysis approach is based on multiparty session types and\n// global graph synthesis POPL'15 (Lange, Tuosto, Yoshida) and CC'16 (Ng,\n// Yoshida).\npackage main\n"
  },
  {
    "path": "examples/altbit/main.go",
    "content": "package main\n\n// Alternating bit - from Milner's Communication and Concurrency\n// with (meaningless) timing\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc main() {\n\ttrans := make(chan int, 1)\n\tack := make(chan int, 1)\n\tgo tx(trans, ack)\n\trx(ack, trans)\n}\n\nfunc flip(b int) int {\n\treturn (b + 1) % 2\n}\n\nfunc tx(snd chan<- int, ack <-chan int) {\n\tb := 0\n\tfor {\n\t\tfmt.Printf(\"tx[%d]: accept\\n\", b)\n\t\tfmt.Printf(\"tx[%d]: send[%d]\\n\", b, b)\n\t\tsnd <- b\n\tSENDING:\n\t\tfor { // SENDING[b]\n\t\t\tselect {\n\t\t\tcase x := <-ack:\n\t\t\t\tif x == b {\n\t\t\t\t\tfmt.Printf(\"tx[%d]: ack[b]\\n\", b)\n\t\t\t\t\tb = flip(b)\n\t\t\t\t\tbreak SENDING // ACCEPT !b\n\t\t\t\t} else {\n\t\t\t\t\tfmt.Printf(\"tx[%d]: ack[!b]\\n\", b)\n\t\t\t\t\tb = flip(b)\n\t\t\t\t\t// SENDING b\n\t\t\t\t}\n\t\t\tcase <-time.After(1 * time.Second):\n\t\t\t\tfmt.Printf(\"tx[%d]: timeout\\n\", b)\n\t\t\t\tfmt.Printf(\"tx[%d]: send[%d]\\n\", b, b)\n\t\t\t\tsnd <- b\n\t\t\t\t// SENDING b\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc rx(reply chan<- int, trans <-chan int) {\n\tb := 1\n\tfor {\n\t\tfmt.Printf(\"rx[%d]: deliver\\n\", b)\n\t\tfmt.Printf(\"rx[%d]: reply[%d]\\n\", b, b)\n\t\treply <- b\n\tREPLYING:\n\t\tfor {\n\t\t\tselect { // REPLYING[b]\n\t\t\tcase x := <-trans:\n\t\t\t\tif x != b {\n\t\t\t\t\tfmt.Printf(\"rx[%d]: trans[!b]\\n\", b)\n\t\t\t\t\tbreak REPLYING // DELIVER !b\n\t\t\t\t} else {\n\t\t\t\t\tfmt.Printf(\"rx[%d]: trans[b]\\n\", b)\n\t\t\t\t\t// REPLYING b\n\t\t\t\t}\n\t\t\tcase <-time.After(1 * time.Second):\n\t\t\t\tfmt.Printf(\"rx[%d]: timeout\\n\", b)\n\t\t\t\tfmt.Printf(\"rx[%d]: reply[%d]\\n\", b, b)\n\t\t\t\treply <- b\n\t\t\t\t// REPLYING b\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "examples/branch-dependent-deadlock/main.go",
    "content": "package main\n\nfunc S(ch chan int, done chan struct{}) {\n\tch <- 1\n\tdone <- struct{}{}\n}\n\nfunc R(ch chan int, done chan struct{}) {\n\t<-ch\n\tdone <- struct{}{}\n}\n\nfunc main() {\n\tdone := make(chan struct{})\n\tfor i := 0; i < 10; i++ {\n\t\tch := make(chan int)\n\t\tif i%2 == 0 {\n\t\t\tgo S(ch, done)\n\t\t} else {\n\t\t\tgo R(ch, done)\n\t\t}\n\t}\n\t<-done\n}\n"
  },
  {
    "path": "examples/channel-scoping-test/main.go",
    "content": "// The reentry example targets multiple use of a function with channel created\n// inside. The channel is anchored in the function, so multiple calls of the\n// function will use different version of the channel(s). Combined with loop\n// indices assumptions this will be inaccurate.\n\npackage main\n\nfunc main() {\n\tch := makenew()\n\tfor i := 0; i < 2; i++ {\n\t\tch2 := makenew()\n\t\tch2 <- 22\n\t}\n\tch <- 42\n}\n\nfunc makenew() chan int {\n\treturn make(chan int, 1)\n}\n"
  },
  {
    "path": "examples/commaok/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\tch := make(chan int, 1)\n\tch <- 1\n\ty := make(map[int]int)\n\tif x, ok := <-ch; ok {\n\t\ty[x] = 1\n\t\tif z, ok := y[1]; ok {\n\t\t\tfmt.Println(z)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "examples/cond-recur/main.go",
    "content": "// Command conditional-recur has a recursion with conditional on one goroutine\n// and another receiving until a done message is received.\npackage main\n\nimport \"fmt\"\n\nfunc x(ch chan int, done chan struct{}) {\n\ti := 0\n\tfor {\n\t\tif i < 3 {\n\t\t\tch <- i\n\t\t\tfmt.Println(\"Sent\", i)\n\t\t\ti++\n\t\t} else {\n\t\t\tdone <- struct{}{}\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc main() {\n\tdone := make(chan struct{})\n\tch := make(chan int)\n\tgo x(ch, done)\nFINISH:\n\tfor {\n\t\tselect {\n\t\tcase x := <-ch:\n\t\t\tfmt.Println(x)\n\t\tcase <-done:\n\t\t\tbreak FINISH\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "examples/deadlocking-philosophers/main.go",
    "content": "package main\n\n// Dining Philosopher modified to include a deadlock.\n// https://github.com/doug/go-dining-philosophers\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"time\"\n)\n\ntype Philosopher struct {\n\tname      string\n\tchopstick chan bool\n\tneighbor  *Philosopher\n}\n\nfunc makePhilosopher(name string, neighbor *Philosopher) *Philosopher {\n\tphil := &Philosopher{name, make(chan bool, 1), neighbor}\n\tphil.chopstick <- true\n\treturn phil\n}\n\nfunc (phil *Philosopher) think() {\n\tfmt.Printf(\"%v is thinking.\\n\", phil.name)\n\ttime.Sleep(time.Duration(rand.Int63n(1e9)))\n}\n\nfunc (phil *Philosopher) eat() {\n\tfmt.Printf(\"%v is eating.\\n\", phil.name)\n\ttime.Sleep(time.Duration(rand.Int63n(1e9)))\n}\n\nfunc (phil *Philosopher) getChopsticks() {\n\ttimeout := make(chan bool, 1)\n\tgo func() { time.Sleep(1e9); timeout <- true }()\n\t<-phil.chopstick\n\tfmt.Printf(\"%v got his chopstick.\\n\", phil.name)\n\t// XXX Deadlock may happen here.\n\tfmt.Printf(\"%v got %v's chopstick.\\n\", phil.name, phil.neighbor.name)\n\tfmt.Printf(\"%v has two chopsticks.\\n\", phil.name)\n\treturn\n}\n\nfunc (phil *Philosopher) returnChopsticks() {\n\tphil.chopstick <- true\n\tphil.neighbor.chopstick <- true\n}\n\nfunc (phil *Philosopher) dine(announce chan *Philosopher) {\n\tphil.think()\n\tphil.getChopsticks()\n\tphil.eat()\n\tphil.returnChopsticks()\n\tannounce <- phil\n}\n\nfunc main() {\n\t//names := []string{\"Kant\", \"Heidegger\", \"Wittgenstein\",\n\t//\t\"Locke\", \"Descartes\", \"Newton\", \"Hume\", \"Leibniz\"}\n\tnames := []string{\"Phil\"}\n\tphilosophers := make([]*Philosopher, len(names))\n\tvar phil *Philosopher\n\tfor i, name := range names {\n\t\tphil = makePhilosopher(name, phil)\n\t\tphilosophers[i] = phil\n\t}\n\tphilosophers[0].neighbor = phil\n\tfmt.Printf(\"There are %v philosophers sitting at a table.\\n\", len(philosophers))\n\tfmt.Println(\"They each have one chopstick, and must borrow from their neighbor to eat.\")\n\tannounce := make(chan *Philosopher)\n\tfor _, phil := range philosophers {\n\t\tgo phil.dine(announce)\n\t}\n\tfor i := 0; i < len(names); i++ {\n\t\tphil := <-announce\n\t\tfmt.Printf(\"%v is done dining.\\n\", phil.name)\n\t}\n}\n"
  },
  {
    "path": "examples/dining-philosophers/main.go",
    "content": "package main\n\n// Dining Philosopher.\n// https://github.com/doug/go-dining-philosophers\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"time\"\n)\n\ntype Philosopher struct {\n\tname      string\n\tchopstick chan bool\n\tneighbor  *Philosopher\n}\n\nfunc makePhilosopher(name string, neighbor *Philosopher) *Philosopher {\n\tphil := &Philosopher{name, make(chan bool, 1), neighbor}\n\tphil.chopstick <- true\n\treturn phil\n}\n\nfunc (phil *Philosopher) think() {\n\tfmt.Printf(\"%v is thinking.\\n\", phil.name)\n\ttime.Sleep(time.Duration(rand.Int63n(1e9)))\n}\n\nfunc (phil *Philosopher) eat() {\n\tfmt.Printf(\"%v is eating.\\n\", phil.name)\n\ttime.Sleep(time.Duration(rand.Int63n(1e9)))\n}\n\nfunc (phil *Philosopher) getChopsticks() {\n\ttimeout := make(chan bool, 1)\n\tgo func() { time.Sleep(1e9); timeout <- true }()\n\t<-phil.chopstick\n\tfmt.Printf(\"%v got his chopstick.\\n\", phil.name)\n\tselect {\n\tcase <-phil.neighbor.chopstick:\n\t\tfmt.Printf(\"%v got %v's chopstick.\\n\", phil.name, phil.neighbor.name)\n\t\tfmt.Printf(\"%v has two chopsticks.\\n\", phil.name)\n\t\treturn\n\tcase <-timeout:\n\t\tphil.chopstick <- true\n\t\tphil.think()\n\t\tphil.getChopsticks()\n\t}\n}\n\nfunc (phil *Philosopher) returnChopsticks() {\n\tphil.chopstick <- true\n\tphil.neighbor.chopstick <- true\n}\n\nfunc (phil *Philosopher) dine(announce chan *Philosopher) {\n\tphil.think()\n\tphil.getChopsticks()\n\tphil.eat()\n\tphil.returnChopsticks()\n\tannounce <- phil\n}\n\nfunc main() {\n\tnames := []string{\"Kant\", \"Heidegger\", \"Wittgenstein\",\n\t\t\"Locke\", \"Descartes\", \"Newton\", \"Hume\", \"Leibniz\"}\n\tphilosophers := make([]*Philosopher, len(names))\n\tvar phil *Philosopher\n\tfor i, name := range names {\n\t\tphil = makePhilosopher(name, phil)\n\t\tphilosophers[i] = phil\n\t}\n\tphilosophers[0].neighbor = phil\n\tfmt.Printf(\"There are %v philosophers sitting at a table.\\n\", len(philosophers))\n\tfmt.Println(\"They each have one chopstick, and must borrow from their neighbor to eat.\")\n\tannounce := make(chan *Philosopher)\n\tfor _, phil := range philosophers {\n\t\tgo phil.dine(announce)\n\t}\n\tfor i := 0; i < len(names); i++ {\n\t\tphil := <-announce\n\t\tfmt.Printf(\"%v is done dining.\\n\", phil.name)\n\t}\n}\n"
  },
  {
    "path": "examples/factorial/main.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tch := make(chan int)\n\tgo fact(5, ch)\n\tfmt.Println(<-ch)\n}\n\nfunc fact(n int, results chan<- int) {\n\tif n <= 1 {\n\t\tresults <- n\n\t\treturn\n\t}\n\tch := make(chan int)\n\tgo fact(n-1, ch)\n\tresults <- n * <-ch\n}\n"
  },
  {
    "path": "examples/fanin-pattern/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n)\n\nfunc work(out chan<- int) {\n\tfor {\n\t\tout <- 42\n\t}\n}\n\nfunc fanin(ch1, ch2 <-chan int) <-chan int {\n\tc := make(chan int)\n\tgo func() {\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase s := <-ch1:\n\t\t\t\tc <- s\n\t\t\tcase s := <-ch2:\n\t\t\t\tc <- s\n\t\t\t}\n\t\t}\n\t}()\n\treturn c\n}\n\nfunc main() {\n\tinput1 := make(chan int)\n\tinput2 := make(chan int)\n\tgo work(input1)\n\tgo work(input2)\n\tc := fanin(input1, input2)\n\tfor {\n\t\tfmt.Println(<-c)\n\t}\n}\n"
  },
  {
    "path": "examples/fanin-pattern-commaok/main.go",
    "content": "package main\n\n// fanin pattern, using for-range loop to consume values (syntactic sugar of\n// loop over r, ok := <-ch)\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc work(out chan<- int) {\n\tfor {\n\t\tout <- 42\n\t}\n}\n\nfunc fanin(ch1, ch2 <-chan int) <-chan int {\n\tc := make(chan int)\n\tgo func(ch1, ch2 <-chan int, c chan<- int) {\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase s := <-ch1:\n\t\t\t\tc <- s\n\t\t\tcase s := <-ch2:\n\t\t\t\tc <- s\n\t\t\tdefault:\n\t\t\t\tclose(c)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}(ch1, ch2, c)\n\treturn c\n}\n\nfunc main() {\n\tinput1 := make(chan int)\n\tinput2 := make(chan int)\n\tgo work(input1)\n\tgo work(input2)\n\ttime.Sleep(1 * time.Second)\n\tfor v := range fanin(input1, input2) {\n\t\tfmt.Println(v)\n\t}\n}\n"
  },
  {
    "path": "examples/fcall/main.go",
    "content": "package main\n\nimport \"fmt\"\n\nvar ch chan int\n\nfunc f() {\n\tfmt.Println(\"blah\")\n\tg()\n}\n\nfunc g() {\n\t<-ch\n\tfmt.Println(\"blah-g\")\n}\n\nfunc main() {\n\tfmt.Println(\"before\")\n\tch = make(chan int)\n\tf()\n\tg()\n\tx := func() <-chan int { return make(chan int) }\n\tselect {\n\tcase <-x():\n\t}\n\tfmt.Println(\"after\")\n}\n"
  },
  {
    "path": "examples/forselect/main.go",
    "content": "// Command nodet-for-select is a for-select pattern between two compatible\n// recursive select.\npackage main\n\nimport \"fmt\"\n\nfunc sel1(ch1, ch2 chan int, done chan struct{}) {\n\tfor {\n\t\tselect {\n\t\tcase <-ch1:\n\t\t\tfmt.Println(\"sel1: recv\")\n\t\t\tdone <- struct{}{}\n\t\t\treturn\n\t\tcase ch2 <- 1:\n\t\t\tfmt.Println(\"sel1: send\")\n\t\t}\n\t}\n}\n\nfunc sel2(ch1, ch2 chan int, done chan struct{}) {\n\tfor {\n\t\tselect {\n\t\tcase <-ch2:\n\t\t\tfmt.Println(\"sel2: recv\")\n\t\tcase ch1 <- 2:\n\t\t\tfmt.Println(\"sel2: send\")\n\t\t\tdone <- struct{}{}\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc main() {\n\tdone := make(chan struct{})\n\ta := make(chan int)\n\tb := make(chan int)\n\tgo sel1(a, b, done)\n\tgo sel2(a, b, done)\n\n\t<-done\n\t<-done\n}\n"
  },
  {
    "path": "examples/giachino-concur14-dining-philosopher/main.go",
    "content": "package main\n\n// Example from CONCUR 14 paper by Giachino et al.\n// doi: 10.1007/978-3-662-44584-6_6\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc Fork(fork chan int) {\n\tfor {\n\t\tfork <- 1\n\t\t<-fork\n\t}\n}\n\n// philosophers (infinite recursive).\nfunc phil(fork1, fork2 chan int, id int) {\n\tvar x1, x2 int\n\tfor {\n\t\tselect {\n\t\tcase x1 = <-fork1:\n\t\t\tselect {\n\t\t\tcase x2 = <-fork2:\n\t\t\t\tfmt.Printf(\"phil %d got both fork\\n\", id)\n\t\t\t\tfork1 <- x1\n\t\t\t\tfork2 <- x2\n\t\t\tdefault:\n\t\t\t\tfork1 <- x1\n\t\t\t}\n\t\tcase x1 = <-fork2:\n\t\t\tselect {\n\t\t\tcase x2 = <-fork1:\n\t\t\t\tfmt.Printf(\"phil %d got both fork\\n\", id)\n\t\t\t\tfork2 <- x1\n\t\t\t\tfork1 <- x2\n\t\t\tdefault:\n\t\t\t\tfork2 <- x1\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc main() {\n\tfork1 := make(chan int)\n\tfork2 := make(chan int)\n\tfork3 := make(chan int)\n\tgo Fork(fork1)\n\tgo Fork(fork2)\n\tgo Fork(fork3)\n\tgo phil(fork1, fork2, 0) // deadlock if phil(fork2, fork1, 0)\n\tgo phil(fork2, fork3, 1)\n\tgo phil(fork3, fork1, 2)\n\ttime.Sleep(10 * time.Second)\n}\n"
  },
  {
    "path": "examples/giachino-concur14-factorial/main.go",
    "content": "package main\n\nimport \"fmt\"\n\n// Example from CONCUR 14 paper by Giachino et al.\n// doi: 10.1007/978-3-662-44584-6_6\n\nfunc fact(n int, r, s chan int) {\n\tif n == 0 {\n\t\tm := <-r\n\t\ts <- m\n\t\treturn\n\t}\n\tt := make(chan int, 1)\n\tfact(n-1, t, s)\n\tm := <-r\n\tt <- m * n\n}\n\nfunc main() {\n\taccumulated, result := make(chan int, 1), make(chan int)\n\tgo fact(3, accumulated, result)\n\taccumulated <- 1\n\tfmt.Println(<-result)\n}\n"
  },
  {
    "path": "examples/github-golang-go-issue-12734/main.go",
    "content": "package main\n\n// This is a bug report from golang/go which the deadlock detector does not\n// detect a deadlock because the net pacakge is loaded (disables detector).\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n)\n\nfunc useless(address string) []byte {\n\thttp.Get(\"https://www.google.com/\")\n\treturn nil\n}\n\nfunc test_a(test_channel chan int) {\n\ttest_channel <- 1\n\treturn\n}\n\nfunc test() {\n\ttest_channel := make(chan int)\n\tfor i := 0; i < 10; i++ {\n\t\tgo test_a(test_channel)\n\t}\n\tfor {\n\t\tfmt.Println(<-test_channel)\n\t}\n}\nfunc main() {\n\ttest()\n}\n"
  },
  {
    "path": "examples/golang-blog-prime-sieve/main.go",
    "content": "// Command golang-blog-prime-sieve is an example from Golang blog.\n// https://golang.org/doc/play/sieve.go\n//\npackage main\n\nimport (\n\t\"fmt\"\n)\n\n// Send the sequence 2, 3, 4, ... to channel 'ch'.\nfunc Generate(ch chan<- int) {\n\tfor i := 2; ; i++ {\n\t\tch <- i // Send 'i' to channel 'ch'.\n\t}\n}\n\n// Copy the values from channel 'in' to channel 'out',\n// removing those divisible by 'prime'.\nfunc Filter(in <-chan int, out chan<- int, prime int) {\n\tfor {\n\t\ti := <-in // Receive value from 'in'.\n\t\tif i%prime != 0 {\n\t\t\tout <- i // Send 'i' to 'out'.\n\t\t}\n\t}\n}\n\n// The prime sieve: Daisy-chain Filter processes.\nfunc main() {\n\tch := make(chan int) // Create a new channel.\n\tgo Generate(ch)      // Launch Generate goroutine.\n\tfor i := 0; i < 10; i++ {\n\t\tprime := <-ch\n\t\tfmt.Println(prime)\n\t\tch1 := make(chan int)\n\t\tgo Filter(ch, ch1, prime)\n\t\tch = ch1\n\t}\n}\n"
  },
  {
    "path": "examples/infinite-prime-sieve/main.go",
    "content": "// Command infinite-primesieve is a modified primesieve example from Golang\n// blog. The program generates an infinite list  (instead of a fixed number) of\n// prime numbers.\n//\n// Original: https://golang.org/doc/play/sieve.go\n//\npackage main\n\nimport (\n\t\"fmt\"\n)\n\n// Send the sequence 2, 3, 4, ... to channel 'ch'.\nfunc Generate(ch chan<- int) {\n\tfor i := 2; ; i++ {\n\t\tch <- i // Send 'i' to channel 'ch'.\n\t}\n}\n\n// Copy the values from channel 'in' to channel 'out',\n// removing those divisible by 'prime'.\nfunc Filter(in <-chan int, out chan<- int, prime int) {\n\tfor {\n\t\ti := <-in // Receive value from 'in'.\n\t\tif i%prime != 0 {\n\t\t\tout <- i // Send 'i' to 'out'.\n\t\t}\n\t}\n}\n\n// The prime sieve: Daisy-chain Filter processes.\nfunc main() {\n\tch := make(chan int) // Create a new channel.\n\tgo Generate(ch)      // Launch Generate goroutine.\n\tfor i := 0; ; i++ {\n\t\tprime := <-ch\n\t\tfmt.Println(prime)\n\t\tch1 := make(chan int)\n\t\tgo Filter(ch, ch1, prime)\n\t\tch = ch1\n\t}\n}\n"
  },
  {
    "path": "examples/issue-10-close-wrong-migo-chan-name/main.go",
    "content": "package main\n\n// Issue #10, when generating migo from programs that uses close, the channel\n// name used is incorrect.\n\nfunc main() {\n\tx := make(chan bool)\n\tclose(x)\n}\n"
  },
  {
    "path": "examples/issue-11-non-communicating-fn-call/main.go",
    "content": "package main\n\n// Issue #11, when a function is empty, all 'call' on that def in a migo file\n// should be scrubbed.\n\nfunc main() {\n\tx := make(chan bool)\n\tgo func() { x <- true }()\n\tif true {\n\t\t<-x\n\t}\n}\n"
  },
  {
    "path": "examples/jobsched/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nvar i int\n\nfunc worker(id int, jobQueue <-chan int, done <-chan struct{}) {\n\tfor {\n\t\tselect {\n\t\tcase jobID := <-jobQueue:\n\t\t\tfmt.Println(id, \"Executing job\", jobID)\n\t\tcase <-done:\n\t\t\tfmt.Println(id, \"Quits\")\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc morejob() bool {\n\ti++\n\treturn i < 20\n}\n\nfunc producer(q chan int, done chan struct{}) {\n\tfor morejob() {\n\t\tq <- i\n\t}\n\tclose(done)\n}\n\nfunc main() {\n\tjobQueue := make(chan int)\n\tdone := make(chan struct{})\n\tgo worker(1, jobQueue, done)\n\tgo worker(2, jobQueue, done)\n\tproducer(jobQueue, done)\n\ttime.Sleep(1 * time.Second)\n}\n"
  },
  {
    "path": "examples/local-deadlock/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc Work() {\n\tfor {\n\t\tfmt.Println(\"Working\")\n\t\ttime.Sleep(1 * time.Second)\n\t}\n}\n\nfunc Send(ch chan<- int)                  { ch <- 42 }\nfunc Recv(ch <-chan int, done chan<- int) { done <- <-ch }\n\nfunc main() {\n\tch, done := make(chan int), make(chan int)\n\tgo Send(ch)\n\tgo Recv(ch, done)\n\tgo Recv(ch, done)\n\tgo Work()\n\n\t<-done\n\t<-done\n}\n"
  },
  {
    "path": "examples/local-deadlock-fixed/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc Work() {\n\tfor {\n\t\tfmt.Println(\"Working\")\n\t\ttime.Sleep(1 * time.Second)\n\t}\n}\n\nfunc Send(ch chan<- int)                  { ch <- 42 }\nfunc Recv(ch <-chan int, done chan<- int) { done <- <-ch }\n\nfunc main() {\n\tch, done := make(chan int), make(chan int)\n\tgo Send(ch)\n\tgo Recv(ch, done)\n\tgo Work()\n\n\t<-done\n}\n"
  },
  {
    "path": "examples/loop-variations/main.go",
    "content": "package main\n\n// This example test different loop and is used for checking loop SSA\n// generation.\n\nimport \"fmt\"\n\nfunc main() {\n\t/*\n\t\t\txs := []int{1, 2, 3}\n\t\t\tfor _, s := range xs {\n\t\t\t\tfmt.Println(s)\n\t\t\t}\n\n\t\t\txs2 := [3]int{1, 2, 3}\n\t\t\tfor _, s := range xs2 {\n\t\t\t\tfmt.Println(s)\n\t\t\t}\n\n\t\t\tfor i := 0; i < 3; i++ {\n\t\t\t\tfmt.Println(\"xs[i]\", xs[i])\n\t\t\t}\n\n\t\t\tfor {\n\t\t\t\tfmt.Println(\"looooopppp one\") // This executes once\n\t\t\t\tbreak\n\t\t\t}\n\n\t\tloopcond := func(i int) bool { return false }\n\n\t\tfor k := 0; loopcond(k); k++ {\n\t\t\tfmt.Println(\"Loop k: \", k)\n\t\t}\n\t*/\n\tfor i := 0; i < 3; i++ {\n\t\tfor j := 0; j < 2; j++ {\n\t\t\tfmt.Printf(\"Index (%d, %d) \", i, j)\n\t\t\tx := make(chan int)\n\t\t\t<-x\n\t\t}\n\t\tfmt.Printf(\"ASBCD\")\n\t}\n\t/*\n\t\tx := []int{1, 2, 3, 4}\n\t\tfor i := range x { // Range loop (safe)\n\t\t\tfmt.Println(i)\n\t\t}\n\n\t\tch := make(chan int)\n\t\tgo func(ch chan int) { ch <- 42; close(ch) }(ch)\n\t\tfor v := range ch {\n\t\t\tfmt.Println(v)\n\t\t}\n\n\t\tfor {\n\t\t\tfmt.Println(\"Infinite looooopppp\")\n\t\t}\n\t*/\n}\n"
  },
  {
    "path": "examples/makechan-in-loop/main.go",
    "content": "// The loop-chan example shows what will happen when a channel is created in a\n// loop (and stored in slices). Pointer analysis identifies the channel\n// operations with channels created inside the loop, but the loop indices are\n// ignored and the analysis must make an assumption that the channels inside the\n// slices are accessed in order.\n\npackage main\n\nfunc main() {\n\tchans := make([]chan int, 5)\n\tfor i := range chans {\n\t\tchans[i] = make(chan int, 1)\n\t}\n\n\tfor _, ch := range chans {\n\t\tch <- 42\n\t}\n\n\tfor _, ch := range chans {\n\t\t<-ch\n\t}\n}\n"
  },
  {
    "path": "examples/md5/main.go",
    "content": "// +build OMIT\n\npackage main\n\nimport (\n\t\"crypto/md5\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"sort\"\n\t\"sync\"\n)\n\n// A result is the product of reading and summing a file using MD5.\ntype result struct {\n\tpath string\n\tsum  [md5.Size]byte\n\terr  error\n}\n\n// sumFiles starts goroutines to walk the directory tree at root and digest each\n// regular file.  These goroutines send the results of the digests on the result\n// channel and send the result of the walk on the error channel.  If done is\n// closed, sumFiles abandons its work.\nfunc sumFiles(done <-chan struct{}, root string) (<-chan result, <-chan error) {\n\t// For each regular file, start a goroutine that sums the file and sends\n\t// the result on c.  Send the result of the walk on errc.\n\tc := make(chan result)\n\terrc := make(chan error, 1)\n\tgo func() { // HL\n\t\tvar wg sync.WaitGroup\n\t\terr := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif !info.Mode().IsRegular() {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\twg.Add(1)\n\t\t\tgo func() { // HL\n\t\t\t\tdata, err := ioutil.ReadFile(path)\n\t\t\t\tselect {\n\t\t\t\tcase c <- result{path, md5.Sum(data), err}: // HL\n\t\t\t\tcase <-done: // HL\n\t\t\t\t}\n\t\t\t\twg.Done()\n\t\t\t}()\n\t\t\t// Abort the walk if done is closed.\n\t\t\tselect {\n\t\t\tcase <-done: // HL\n\t\t\t\treturn errors.New(\"walk canceled\")\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t})\n\t\t// Walk has returned, so all calls to wg.Add are done.  Start a\n\t\t// goroutine to close c once all the sends are done.\n\t\tgo func() { // HL\n\t\t\twg.Wait()\n\t\t\tclose(c) // HL\n\t\t}()\n\t\t// No select needed here, since errc is buffered.\n\t\terrc <- err // HL\n\t}()\n\treturn c, errc\n}\n\n// MD5All reads all the files in the file tree rooted at root and returns a map\n// from file path to the MD5 sum of the file's contents.  If the directory walk\n// fails or any read operation fails, MD5All returns an error.  In that case,\n// MD5All does not wait for inflight read operations to complete.\nfunc MD5All(root string) (map[string][md5.Size]byte, error) {\n\t// MD5All closes the done channel when it returns; it may do so before\n\t// receiving all the values from c and errc.\n\tdone := make(chan struct{}) // HLdone\n\tdefer close(done)           // HLdone\n\n\tc, errc := sumFiles(done, root) // HLdone\n\n\tm := make(map[string][md5.Size]byte)\n\tfor r := range c { // HLrange\n\t\tif r.err != nil {\n\t\t\treturn nil, r.err\n\t\t}\n\t\tm[r.path] = r.sum\n\t}\n\tif err := <-errc; err != nil {\n\t\treturn nil, err\n\t}\n\treturn m, nil\n}\n\nfunc main() {\n\t// Calculate the MD5 sum of all files under the specified directory,\n\t// then print the results sorted by path name.\n\tm, err := MD5All(os.Args[1])\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\treturn\n\t}\n\tvar paths []string\n\tfor path := range m {\n\t\tpaths = append(paths, path)\n\t}\n\tsort.Strings(paths)\n\tfor _, path := range paths {\n\t\tfmt.Printf(\"%x  %s\\n\", m[path], path)\n\t}\n}\n"
  },
  {
    "path": "examples/multi-makechan-same-var/main.go",
    "content": "// createchan is an example which reuses a channel for different operations in\n// an expanded form.\n\npackage main\n\nfunc createChan() chan int {\n\treturn make(chan int, 1)\n}\n\nfunc main() {\n\tch := createChan()\n\tch <- 42\n\n\tch = createChan()\n\tch <- 3\n}\n"
  },
  {
    "path": "examples/multiple-files/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\tfmt.Println(\"Do stuff\")\n\tx()\n\tfmt.Println(\"Do more stuff\")\n}\n"
  },
  {
    "path": "examples/multiple-files/x.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc x() {\n\tfmt.Println(\"Hello X\")\n}\n"
  },
  {
    "path": "examples/multiple-timeout/main.go",
    "content": "// Command multiple-timeout is an example which uses multiple branches of\n// time.After.\n//\n// The main purpose is to test if the extracted local type can distinguish\n// between two \"externally created\" channels (in time.After), and are\n// initialised separately in the local graph.\npackage main\n\nimport \"time\"\n\nfunc main() {\n\tch := make(chan int, 1)\n\tgo func(ch chan int) { time.Sleep(10 * time.Second); ch <- 42 }(ch)\n\n\tselect {\n\tcase <-ch:\n\tcase <-time.After(2 * time.Second):\n\tcase <-time.After(4 * time.Second):\n\t}\n}\n"
  },
  {
    "path": "examples/parallel-buffered-recursive-fibonacci/main.go",
    "content": "// Command parallel-recursive-fibonacci is a recursive fibonacci which spawns a\n// new goroutine per fib call.\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tch := make(chan int)\n\tgo fib(10, ch)\n\tfmt.Println(<-ch)\n}\n\nfunc fib(n int, ch chan<- int) {\n\tif n <= 1 {\n\t\tch <- n\n\t\treturn\n\t}\n\tch1 := make(chan int, 2)\n\tgo fib(n-1, ch1)\n\tgo fib(n-2, ch1)\n\tch <- <-ch1 + <-ch1\n}\n"
  },
  {
    "path": "examples/parallel-recursive-fibonacci/main.go",
    "content": "// Command parallel-recursive-fibonacci is a recursive fibonacci which spawns a\n// new goroutine per fib call.\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tch := make(chan int)\n\tgo fib(10, ch)\n\tfmt.Println(<-ch)\n}\n\nfunc fib(n int, ch chan<- int) {\n\tif n <= 1 {\n\t\tch <- n\n\t\treturn\n\t}\n\tch1 := make(chan int)\n\tch2 := make(chan int)\n\tgo fib(n-1, ch1)\n\tgo fib(n-2, ch2)\n\tch <- <-ch1 + <-ch2\n}\n"
  },
  {
    "path": "examples/parallel-twoprocess-fibonacci/main.go",
    "content": "// Command parallel-twoprocess-fibonacci is an improved version of parallel\n// fibonacci which limits to only spawning 2 goroutines.\npackage main\n\nimport \"fmt\"\n\nfunc fib(n int) int {\n\tif n <= 1 {\n\t\treturn n\n\t}\n\treturn fib(n-1) + fib(n-2)\n}\n\nfunc fibParallel(n int, ch chan<- int) {\n\tch <- fib(n)\n}\n\nfunc main() {\n\tch1 := make(chan int)\n\tch2 := make(chan int)\n\tn := 10\n\tgo fibParallel(n-1, ch1)\n\tgo fibParallel(n-2, ch2)\n\n\tfmt.Println(<-ch1 + <-ch2)\n}\n"
  },
  {
    "path": "examples/philo/main.go",
    "content": "package main\n\n// philio test case from Stadtmuller, Thieman\n\nimport (\n\t\"fmt\"\n)\n\nfunc philo(id int, forks chan int) {\n\tfor {\n\t\t<-forks\n\t\t<-forks\n\t\tfmt.Printf(\"%d eats\\n\", id)\n\t\tforks <- 1\n\t\tforks <- 1\n\t}\n}\n\nfunc main() {\n\tforks := make(chan int)\n\tgo func() { forks <- 1 }()\n\tgo func() { forks <- 1 }()\n\tgo func() { forks <- 1 }()\n\tgo philo(1, forks)\n\tgo philo(2, forks)\n\tphilo(3, forks)\n}\n"
  },
  {
    "path": "examples/popl17/alt-bit/main.go",
    "content": "package main\n\n// Alternating bit - from Milner's Communication and Concurrency\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\ttrans := make(chan int, 1)\n\tack := make(chan int, 1)\n\tgo tx(trans, ack)\n\trx(ack, trans)\n}\n\nfunc tx(snd chan<- int, ack <-chan int) {\n\tb := 0\n\tfor {\n\t\tfmt.Printf(\"tx[%d]: accept\\n\", b)\n\t\tfmt.Printf(\"tx[%d]: send[%d]\\n\", b, b)\n\t\tsnd <- b\n\tSENDING:\n\t\tfor { // SENDING[b]\n\t\t\tselect {\n\t\t\tcase x := <-ack:\n\t\t\t\tif x == b {\n\t\t\t\t\tfmt.Printf(\"tx[%d]: ack[b]\\n\", b)\n\t\t\t\t\tb = (b + 1) % 2\n\t\t\t\t\tbreak SENDING // ACCEPT !b\n\t\t\t\t} else {\n\t\t\t\t\tfmt.Printf(\"tx[%d]: ack[!b]\\n\", b)\n\t\t\t\t\tb = (b + 1) % 2\n\t\t\t\t\t// SENDING b\n\t\t\t\t}\n\t\t\tcase snd <- b:\n\t\t\t\tfmt.Printf(\"tx[%d]: timeout\\n\", b)\n\t\t\t\tfmt.Printf(\"tx[%d]: send[%d]\\n\", b, b)\n\t\t\t\t// SENDING b\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc rx(reply chan<- int, trans <-chan int) {\n\tb := 1\n\tfor {\n\t\tfmt.Printf(\"rx[%d]: deliver\\n\", b)\n\t\tfmt.Printf(\"rx[%d]: reply[%d]\\n\", b, b)\n\t\treply <- b\n\tREPLYING:\n\t\tfor {\n\t\t\tselect { // REPLYING[b]\n\t\t\tcase x := <-trans:\n\t\t\t\tif x != b {\n\t\t\t\t\tfmt.Printf(\"rx[%d]: trans[!b]\\n\", b)\n\t\t\t\t\tbreak REPLYING // DELIVER !b\n\t\t\t\t} else {\n\t\t\t\t\tfmt.Printf(\"rx[%d]: trans[b]\\n\", b)\n\t\t\t\t\t// REPLYING b\n\t\t\t\t}\n\t\t\tcase reply <- b:\n\t\t\t\tfmt.Printf(\"rx[%d]: timeout\\n\", b)\n\t\t\t\tfmt.Printf(\"rx[%d]: reply[%d]\\n\", b, b)\n\t\t\t\t// REPLYING b\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "examples/popl17/concsys/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"time\"\n)\n\ntype Result string\ntype Search func(query string) Result\n\nfunc fakeSearch(kind string) Search {\n\treturn func(query string) Result {\n\t\ttime.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)\n\t\treturn Result(fmt.Sprintf(\"%s result for %q\\n\", kind, query))\n\t}\n}\n\nvar (\n\tWeb    = fakeSearch(\"web\")\n\tWeb1   = fakeSearch(\"web-1\")\n\tWeb2   = fakeSearch(\"web-2\")\n\tImage  = fakeSearch(\"image\")\n\tImage1 = fakeSearch(\"image-1\")\n\tImage2 = fakeSearch(\"image-2\")\n\tImage3 = fakeSearch(\"image-3\")\n\tVideo  = fakeSearch(\"video\")\n\tVideo1 = fakeSearch(\"video-1\")\n\tVideo2 = fakeSearch(\"video-2\")\n\tVideo3 = fakeSearch(\"video-3\")\n)\n\nfunc SequentialSearch(query string) (results []Result) {\n\tresults = append(results, Web(query))\n\tresults = append(results, Image(query))\n\tresults = append(results, Video(query))\n\treturn\n}\n\nfunc ConcurrentSearch(query string) (results []Result) {\n\tc := make(chan Result)\n\tgo func(c chan Result) { c <- Web(query) }(c)\n\tgo func(c chan Result) { c <- Image(query) }(c)\n\tgo func(c chan Result) { c <- Video(query) }(c)\n\n\tfor i := 0; i < 3; i++ {\n\t\tresult := <-c\n\t\tresults = append(results, result)\n\t}\n\treturn\n}\n\nfunc ConcurrentSearchWithCutOff(query string) (results []Result) {\n\tc := make(chan Result)\n\tgo func() { c <- Web(query) }()\n\tgo func() { c <- Image(query) }()\n\tgo func() { c <- Video(query) }()\n\n\ttimeout := time.After(80 * time.Millisecond)\n\tfor i := 0; i < 3; i++ { // for each goroutine that is ready pick up results\n\t\tselect {\n\t\tcase result := <-c:\n\t\t\tresults = append(results, result)\n\t\tcase <-timeout:\n\t\t\tfmt.Println(\"timed out\")\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n\nfunc First(query string, replicas ...Search) Result {\n\tc := make(chan Result, 1)\n\tsearchReplica := func(i int) { c <- replicas[i](query) }\n\tfor i := range replicas {\n\t\tsearchReplica(i)\n\t}\n\treturn <-c\n}\n\nfunc ReplicaSearch(query string) (results []Result) {\n\tc := make(chan Result)\n\tgo func(c chan Result) { c <- Web1(query) }(c)\n\tgo func(c chan Result) { c <- Image1(query) }(c)\n\tgo func(c chan Result) { c <- Video1(query) }(c)\n\n\ttimeout := time.After(80 * time.Millisecond)\n\t//for i := 0; i < 3; i++ {\n\tselect {\n\tcase result := <-c:\n\t\tresults = append(results, result)\n\tcase <-timeout:\n\t\tfmt.Println(\"seach timed out\")\n\t\treturn\n\t}\n\t//}\n\treturn\n}\n\nfunc main() {\n\trand.Seed(time.Now().UnixNano())\n\tstart := time.Now()\n\n\tresults := SequentialSearch(\"golang\")\n\t//results := ConcurrentSearch(\"golang\")\n\t//\tresults := ConcurrentSearchWithCutOff(\"golang\")\n\t//\tresults := First(\"golang\", fakeSearch(\"replica-1\"), fakeSearch(\"replica-2\"))\n\tresults = ReplicaSearch(\"golang\")\n\n\telapsed := time.Since(start)\n\tfmt.Println(results)\n\tfmt.Println(elapsed)\n}\n"
  },
  {
    "path": "examples/popl17/cond-recur/main.go",
    "content": "// Command conditional-recur has a recursion with conditional on one goroutine\n// and another receiving until a done message is received.\npackage main\n\nimport \"fmt\"\n\nfunc x(ch chan int, done chan struct{}) {\n\ti := 0\n\tfor {\n\t\tif i < 3 {\n\t\t\tch <- i\n\t\t\tfmt.Println(\"Sent\", i)\n\t\t\ti++\n\t\t} else {\n\t\t\tdone <- struct{}{}\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc main() {\n\tdone := make(chan struct{})\n\tch := make(chan int)\n\tgo x(ch, done)\nFINISH:\n\tfor {\n\t\tselect {\n\t\tcase x := <-ch:\n\t\t\tfmt.Println(x)\n\t\tcase <-done:\n\t\t\tbreak FINISH\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "examples/popl17/dinephil/main.go",
    "content": "package main\n\n// Example from CONCUR 14 paper by Giachino et al.\n// doi: 10.1007/978-3-662-44584-6_6\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc Fork(fork chan int) {\n\tfor {\n\t\tfork <- 1\n\t\t<-fork\n\t}\n}\n\n// philosophers (infinite recursive).\nfunc phil(fork1, fork2 chan int, id int) {\n\tvar x1, x2 int\n\tfor {\n\t\tselect {\n\t\tcase x1 = <-fork1:\n\t\t\tselect {\n\t\t\tcase x2 = <-fork2:\n\t\t\t\tfmt.Printf(\"phil %d got both fork\\n\", id)\n\t\t\t\tfork1 <- x1\n\t\t\t\tfork2 <- x2\n\t\t\tdefault:\n\t\t\t\tfork1 <- x1\n\t\t\t}\n\t\tcase x1 = <-fork2:\n\t\t\tselect {\n\t\t\tcase x2 = <-fork1:\n\t\t\t\tfmt.Printf(\"phil %d got both fork\\n\", id)\n\t\t\t\tfork2 <- x1\n\t\t\t\tfork1 <- x2\n\t\t\tdefault:\n\t\t\t\tfork2 <- x1\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc main() {\n\tfork1 := make(chan int)\n\tfork2 := make(chan int)\n\tfork3 := make(chan int)\n\tgo phil(fork1, fork2, 0) // deadlock if phil(fork2, fork1, 0)\n\tgo phil(fork2, fork3, 1)\n\tgo phil(fork3, fork1, 2)\n\tgo Fork(fork1)\n\tgo Fork(fork2)\n\tgo Fork(fork3)\n\ttime.Sleep(10 * time.Second)\n}\n"
  },
  {
    "path": "examples/popl17/fact/main.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tch := make(chan int)\n\tgo fact(5, ch)\n\tfmt.Println(<-ch)\n}\n\nfunc fact(n int, results chan<- int) {\n\tif n <= 1 {\n\t\tresults <- n\n\t\treturn\n\t}\n\tch := make(chan int)\n\tgo fact(n-1, ch)\n\tresults <- n * <-ch\n}\n"
  },
  {
    "path": "examples/popl17/fanin/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n)\n\nfunc work1(out chan<- int) {\n\tfor {\n\t\tout <- 42\n\t}\n}\nfunc work2(out chan<- int) {\n\tfor {\n\t\tout <- 42\n\t}\n}\n\nfunc fanin(ch1, ch2, c chan int) {\n\tgo func(ch1, ch2, c chan int) {\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase s := <-ch1:\n\t\t\t\tc <- s\n\t\t\tcase s := <-ch2:\n\t\t\t\tc <- s\n\t\t\t}\n\t\t}\n\t}(ch1, ch2, c)\n\tfor {\n\t\tfmt.Println(<-c)\n\t}\n}\n\nfunc main() {\n\tinput1 := make(chan int)\n\tinput2 := make(chan int)\n\tgo work1(input1)\n\tgo work2(input2)\n\tc := make(chan int)\n\tfanin(input1, input2, c)\n}\n"
  },
  {
    "path": "examples/popl17/fanin-alt/main.go",
    "content": "package main\n\n// fanin pattern, using for-range loop to consume values (syntactic sugar of\n// loop over r, ok := <-ch)\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc work(out chan<- int) {\n\tfor {\n\t\tout <- 42\n\t}\n}\n\nfunc fanin(ch1, ch2, c chan int) {\n\tgo func(ch1, ch2, c chan int) {\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase s := <-ch1:\n\t\t\t\tc <- s\n\t\t\tcase s := <-ch2:\n\t\t\t\tc <- s\n\t\t\tdefault:\n\t\t\t\tclose(c)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}(ch1, ch2, c)\n\tfor v := range c {\n\t\tfmt.Println(v)\n\t}\n}\n\nfunc main() {\n\tinput1 := make(chan int)\n\tinput2 := make(chan int)\n\tgo work(input1)\n\tgo work(input2)\n\tc := make(chan int)\n\tfanin(input1, input2, c)\n\ttime.Sleep(1 * time.Second)\n}\n"
  },
  {
    "path": "examples/popl17/fib/main.go",
    "content": "// Command parallel-recursive-fibonacci is a recursive fibonacci which spawns a\n// new goroutine per fib call.\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tch := make(chan int)\n\tgo fib(10, ch)\n\tfmt.Println(<-ch)\n}\n\nfunc fib(n int, ch chan<- int) {\n\tif n <= 1 {\n\t\tch <- n\n\t\treturn\n\t}\n\tch1 := make(chan int)\n\tch2 := make(chan int)\n\tgo fib(n-1, ch1)\n\tgo fib(n-2, ch2)\n\tch <- <-ch1 + <-ch2\n}\n"
  },
  {
    "path": "examples/popl17/fib-async/main.go",
    "content": "// Command parallel-recursive-fibonacci is a recursive fibonacci which spawns a\n// new goroutine per fib call.\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tch := make(chan int)\n\tgo fib(10, ch)\n\tfmt.Println(<-ch)\n}\n\nfunc fib(n int, ch chan<- int) {\n\tif n <= 1 {\n\t\tch <- n\n\t\treturn\n\t}\n\tch1 := make(chan int, 2)\n\tgo fib(n-1, ch1)\n\tgo fib(n-2, ch1)\n\tch <- <-ch1 + <-ch1\n}\n"
  },
  {
    "path": "examples/popl17/fixed/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc Work() {\n\tfor {\n\t\tfmt.Println(\"Working\")\n\t\ttime.Sleep(1 * time.Second)\n\t}\n}\n\nfunc Send(ch chan<- int)                  { ch <- 42 }\nfunc Recv(ch <-chan int, done chan<- int) { done <- <-ch }\n\nfunc main() {\n\tch, done := make(chan int), make(chan int)\n\tgo Send(ch)\n\tgo Recv(ch, done)\n\tgo Work()\n\n\t<-done\n}\n"
  },
  {
    "path": "examples/popl17/forselect/main.go",
    "content": "// Command nodet-for-select is a for-select pattern between two compatible\n// recursive select.\npackage main\n\nimport \"fmt\"\n\nfunc sel1(ch1, ch2 chan int, done chan struct{}) {\n\tfor {\n\t\tselect {\n\t\tcase <-ch1:\n\t\t\tfmt.Println(\"sel1: recv\")\n\t\t\tdone <- struct{}{}\n\t\t\treturn\n\t\tcase ch2 <- 1:\n\t\t\tfmt.Println(\"sel1: send\")\n\t\t}\n\t}\n}\n\nfunc sel2(ch1, ch2 chan int, done chan struct{}) {\n\tfor {\n\t\tselect {\n\t\tcase <-ch2:\n\t\t\tfmt.Println(\"sel2: recv\")\n\t\tcase ch1 <- 2:\n\t\t\tfmt.Println(\"sel2: send\")\n\t\t\tdone <- struct{}{}\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc main() {\n\tdone := make(chan struct{})\n\ta := make(chan int)\n\tb := make(chan int)\n\tgo sel1(a, b, done)\n\tgo sel2(a, b, done)\n\n\t<-done\n\t<-done\n}\n"
  },
  {
    "path": "examples/popl17/jobsched/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nvar i int\n\nfunc worker(id int, jobQueue <-chan int, done <-chan struct{}) {\n\tfor {\n\t\tselect {\n\t\tcase jobID := <-jobQueue:\n\t\t\tfmt.Println(id, \"Executing job\", jobID)\n\t\tcase <-done:\n\t\t\tfmt.Println(id, \"Quits\")\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc morejob() bool {\n\ti++\n\treturn i < 20\n}\n\nfunc producer(q chan int, done chan struct{}) {\n\tfor morejob() {\n\t\tq <- 42\n\t}\n\tclose(done)\n}\n\nfunc main() {\n\tjobQueue := make(chan int)\n\tdone := make(chan struct{})\n\tgo worker(1, jobQueue, done)\n\tgo worker(2, jobQueue, done)\n\tproducer(jobQueue, done)\n\ttime.Sleep(1 * time.Second)\n}\n"
  },
  {
    "path": "examples/popl17/mismatch/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc Work() {\n\tfor {\n\t\tfmt.Println(\"Working\")\n\t\ttime.Sleep(1 * time.Second)\n\t}\n}\n\nfunc Send(ch chan<- int)                  { ch <- 42 }\nfunc Recv(ch <-chan int, done chan<- int) { done <- <-ch }\n\nfunc main() {\n\tch, done := make(chan int), make(chan int)\n\tgo Send(ch)\n\tgo Recv(ch, done)\n\tgo Recv(ch, done)\n\tgo Work()\n\n\t<-done\n\t<-done\n}\n"
  },
  {
    "path": "examples/popl17/sieve/main.go",
    "content": "// Command infinite-primesieve is a modified primesieve example from Golang\n// blog. The program generates an infinite list  (instead of a fixed number) of\n// prime numbers.\n//\n// Original: https://golang.org/doc/play/sieve.go\n//\npackage main\n\nimport (\n\t\"fmt\"\n)\n\n// Send the sequence 2, 3, 4, ... to channel 'ch'.\nfunc Generate(ch chan<- int) {\n\tfor i := 2; ; i++ {\n\t\tch <- i // Send 'i' to channel 'ch'.\n\t}\n}\n\n// Copy the values from channel 'in' to channel 'out',\n// removing those divisible by 'prime'.\nfunc Filter(in <-chan int, out chan<- int, prime int) {\n\tfor {\n\t\ti := <-in // Receive value from 'in'.\n\t\tif i%prime != 0 {\n\t\t\tout <- i // Send 'i' to 'out'.\n\t\t}\n\t}\n}\n\n// The prime sieve: Daisy-chain Filter processes.\nfunc main() {\n\tch := make(chan int) // Create a new channel.\n\tgo Generate(ch)      // Launch Generate goroutine.\n\tfor i := 0; ; i++ {\n\t\tprime := <-ch\n\t\tfmt.Println(prime)\n\t\tch1 := make(chan int)\n\t\tgo Filter(ch, ch1, prime)\n\t\tch = ch1\n\t}\n}\n"
  },
  {
    "path": "examples/popl17ae/close/main.go",
    "content": "package main\n\nfunc main() {\n\tch := make(chan int)\n\tclose(ch)\n}\n"
  },
  {
    "path": "examples/popl17ae/emptyselect/main.go",
    "content": "package main\n\nfunc s(ch chan int) {\n\tch <- 5\n}\n\nfunc main() {\n\tch := make(chan int, 2)\n\tselect {\n\tcase <-ch:\n\tdefault:\n\t}\n\ts(ch)\n}\n"
  },
  {
    "path": "examples/powsers/powser1.go",
    "content": "// run\n\n// +build ignore\n\n// Copyright 2009 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// Test concurrency primitives: power series.\n\n// Power series package\n// A power series is a channel, along which flow rational\n// coefficients.  A denominator of zero signifies the end.\n// Original code in Newsqueak by Doug McIlroy.\n// See Squinting at Power Series by Doug McIlroy,\n//   http://www.cs.bell-labs.com/who/rsc/thread/squint.pdf\n\npackage main\n\nimport \"os\"\n\ntype rat struct {\n\tnum, den int64 // numerator, denominator\n}\n\nfunc (u rat) pr() {\n\tif u.den == 1 {\n\t\tprint(u.num)\n\t} else {\n\t\tprint(u.num, \"/\", u.den)\n\t}\n\tprint(\" \")\n}\n\nfunc (u rat) eq(c rat) bool {\n\treturn u.num == c.num && u.den == c.den\n}\n\ntype dch struct {\n\treq chan int\n\tdat chan rat\n\tnam int\n}\n\ntype dch2 [2]*dch\n\nvar chnames string\nvar chnameserial int\nvar seqno int\n\nfunc mkdch() *dch {\n\tc := chnameserial % len(chnames)\n\tchnameserial++\n\td := new(dch)\n\td.req = make(chan int)\n\td.dat = make(chan rat)\n\td.nam = c\n\treturn d\n}\n\nfunc mkdch2() *dch2 {\n\td2 := new(dch2)\n\td2[0] = mkdch()\n\td2[1] = mkdch()\n\treturn d2\n}\n\n// split reads a single demand channel and replicates its\n// output onto two, which may be read at different rates.\n// A process is created at first demand for a rat and dies\n// after the rat has been sent to both outputs.\n\n// When multiple generations of split exist, the newest\n// will service requests on one channel, which is\n// always renamed to be out[0]; the oldest will service\n// requests on the other channel, out[1].  All generations but the\n// newest hold queued data that has already been sent to\n// out[0].  When data has finally been sent to out[1],\n// a signal on the release-wait channel tells the next newer\n// generation to begin servicing out[1].\n\nfunc dosplit(in *dch, out *dch2, wait chan int) {\n\tboth := false // do not service both channels\n\n\tselect {\n\tcase <-out[0].req:\n\n\tcase <-wait:\n\t\tboth = true\n\t\tselect {\n\t\tcase <-out[0].req:\n\n\t\tcase <-out[1].req:\n\t\t\tout[0], out[1] = out[1], out[0]\n\t\t}\n\t}\n\n\tseqno++\n\tin.req <- seqno\n\trelease := make(chan int)\n\tgo dosplit(in, out, release)\n\tdat := <-in.dat\n\tout[0].dat <- dat\n\tif !both {\n\t\t<-wait\n\t}\n\t<-out[1].req\n\tout[1].dat <- dat\n\trelease <- 0\n}\n\nfunc split(in *dch, out *dch2) {\n\trelease := make(chan int)\n\tgo dosplit(in, out, release)\n\trelease <- 0\n}\n\nfunc put(dat rat, out *dch) {\n\t<-out.req\n\tout.dat <- dat\n}\n\nfunc get(in *dch) rat {\n\tseqno++\n\tin.req <- seqno\n\treturn <-in.dat\n}\n\n// Get one rat from each of n demand channels\n\nfunc getn(in []*dch) []rat {\n\tn := len(in)\n\tif n != 2 {\n\t\tpanic(\"bad n in getn\")\n\t}\n\treq := new([2]chan int)\n\tdat := new([2]chan rat)\n\tout := make([]rat, 2)\n\tvar i int\n\tvar it rat\n\tfor i = 0; i < n; i++ {\n\t\treq[i] = in[i].req\n\t\tdat[i] = nil\n\t}\n\tfor n = 2 * n; n > 0; n-- {\n\t\tseqno++\n\n\t\tselect {\n\t\tcase req[0] <- seqno:\n\t\t\tdat[0] = in[0].dat\n\t\t\treq[0] = nil\n\t\tcase req[1] <- seqno:\n\t\t\tdat[1] = in[1].dat\n\t\t\treq[1] = nil\n\t\tcase it = <-dat[0]:\n\t\t\tout[0] = it\n\t\t\tdat[0] = nil\n\t\tcase it = <-dat[1]:\n\t\t\tout[1] = it\n\t\t\tdat[1] = nil\n\t\t}\n\t}\n\treturn out\n}\n\n// Get one rat from each of 2 demand channels\n\nfunc get2(in0 *dch, in1 *dch) []rat {\n\treturn getn([]*dch{in0, in1})\n}\n\nfunc copy(in *dch, out *dch) {\n\tfor {\n\t\t<-out.req\n\t\tout.dat <- get(in)\n\t}\n}\n\nfunc repeat(dat rat, out *dch) {\n\tfor {\n\t\tput(dat, out)\n\t}\n}\n\ntype PS *dch    // power series\ntype PS2 *[2]PS // pair of power series\n\nvar Ones PS\nvar Twos PS\n\nfunc mkPS() *dch {\n\treturn mkdch()\n}\n\nfunc mkPS2() *dch2 {\n\treturn mkdch2()\n}\n\n// Conventions\n// Upper-case for power series.\n// Lower-case for rationals.\n// Input variables: U,V,...\n// Output variables: ...,Y,Z\n\n// Integer gcd; needed for rational arithmetic\n\nfunc gcd(u, v int64) int64 {\n\tif u < 0 {\n\t\treturn gcd(-u, v)\n\t}\n\tif u == 0 {\n\t\treturn v\n\t}\n\treturn gcd(v%u, u)\n}\n\n// Make a rational from two ints and from one int\n\nfunc i2tor(u, v int64) rat {\n\tg := gcd(u, v)\n\tvar r rat\n\tif v > 0 {\n\t\tr.num = u / g\n\t\tr.den = v / g\n\t} else {\n\t\tr.num = -u / g\n\t\tr.den = -v / g\n\t}\n\treturn r\n}\n\nfunc itor(u int64) rat {\n\treturn i2tor(u, 1)\n}\n\nvar zero rat\nvar one rat\n\n// End mark and end test\n\nvar finis rat\n\nfunc end(u rat) int64 {\n\tif u.den == 0 {\n\t\treturn 1\n\t}\n\treturn 0\n}\n\n// Operations on rationals\n\nfunc add(u, v rat) rat {\n\tg := gcd(u.den, v.den)\n\treturn i2tor(u.num*(v.den/g)+v.num*(u.den/g), u.den*(v.den/g))\n}\n\nfunc mul(u, v rat) rat {\n\tg1 := gcd(u.num, v.den)\n\tg2 := gcd(u.den, v.num)\n\tvar r rat\n\tr.num = (u.num / g1) * (v.num / g2)\n\tr.den = (u.den / g2) * (v.den / g1)\n\treturn r\n}\n\nfunc neg(u rat) rat {\n\treturn i2tor(-u.num, u.den)\n}\n\nfunc sub(u, v rat) rat {\n\treturn add(u, neg(v))\n}\n\nfunc inv(u rat) rat { // invert a rat\n\tif u.num == 0 {\n\t\tpanic(\"zero divide in inv\")\n\t}\n\treturn i2tor(u.den, u.num)\n}\n\n// print eval in floating point of PS at x=c to n terms\nfunc evaln(c rat, U PS, n int) {\n\txn := float64(1)\n\tx := float64(c.num) / float64(c.den)\n\tval := float64(0)\n\tfor i := 0; i < n; i++ {\n\t\tu := get(U)\n\t\tif end(u) != 0 {\n\t\t\tbreak\n\t\t}\n\t\tval = val + x*float64(u.num)/float64(u.den)\n\t\txn = xn * x\n\t}\n\tprint(val, \"\\n\")\n}\n\n// Print n terms of a power series\nfunc printn(U PS, n int) {\n\tdone := false\n\tfor ; !done && n > 0; n-- {\n\t\tu := get(U)\n\t\tif end(u) != 0 {\n\t\t\tdone = true\n\t\t} else {\n\t\t\tu.pr()\n\t\t}\n\t}\n\tprint((\"\\n\"))\n}\n\n// Evaluate n terms of power series U at x=c\nfunc eval(c rat, U PS, n int) rat {\n\tif n == 0 {\n\t\treturn zero\n\t}\n\ty := get(U)\n\tif end(y) != 0 {\n\t\treturn zero\n\t}\n\treturn add(y, mul(c, eval(c, U, n-1)))\n}\n\n// Power-series constructors return channels on which power\n// series flow.  They start an encapsulated generator that\n// puts the terms of the series on the channel.\n\n// Make a pair of power series identical to a given power series\n\nfunc Split(U PS) *dch2 {\n\tUU := mkdch2()\n\tgo split(U, UU)\n\treturn UU\n}\n\n// Add two power series\nfunc Add(U, V PS) PS {\n\tZ := mkPS()\n\tgo func() {\n\t\tvar uv []rat\n\t\tfor {\n\t\t\t<-Z.req\n\t\t\tuv = get2(U, V)\n\t\t\tswitch end(uv[0]) + 2*end(uv[1]) {\n\t\t\tcase 0:\n\t\t\t\tZ.dat <- add(uv[0], uv[1])\n\t\t\tcase 1:\n\t\t\t\tZ.dat <- uv[1]\n\t\t\t\tcopy(V, Z)\n\t\t\tcase 2:\n\t\t\t\tZ.dat <- uv[0]\n\t\t\t\tcopy(U, Z)\n\t\t\tcase 3:\n\t\t\t\tZ.dat <- finis\n\t\t\t}\n\t\t}\n\t}()\n\treturn Z\n}\n\n// Multiply a power series by a constant\nfunc Cmul(c rat, U PS) PS {\n\tZ := mkPS()\n\tgo func() {\n\t\tdone := false\n\t\tfor !done {\n\t\t\t<-Z.req\n\t\t\tu := get(U)\n\t\t\tif end(u) != 0 {\n\t\t\t\tdone = true\n\t\t\t} else {\n\t\t\t\tZ.dat <- mul(c, u)\n\t\t\t}\n\t\t}\n\t\tZ.dat <- finis\n\t}()\n\treturn Z\n}\n\n// Subtract\n\nfunc Sub(U, V PS) PS {\n\treturn Add(U, Cmul(neg(one), V))\n}\n\n// Multiply a power series by the monomial x^n\n\nfunc Monmul(U PS, n int) PS {\n\tZ := mkPS()\n\tgo func() {\n\t\tfor ; n > 0; n-- {\n\t\t\tput(zero, Z)\n\t\t}\n\t\tcopy(U, Z)\n\t}()\n\treturn Z\n}\n\n// Multiply by x\n\nfunc Xmul(U PS) PS {\n\treturn Monmul(U, 1)\n}\n\nfunc Rep(c rat) PS {\n\tZ := mkPS()\n\tgo repeat(c, Z)\n\treturn Z\n}\n\n// Monomial c*x^n\n\nfunc Mon(c rat, n int) PS {\n\tZ := mkPS()\n\tgo func() {\n\t\tif c.num != 0 {\n\t\t\tfor ; n > 0; n = n - 1 {\n\t\t\t\tput(zero, Z)\n\t\t\t}\n\t\t\tput(c, Z)\n\t\t}\n\t\tput(finis, Z)\n\t}()\n\treturn Z\n}\n\nfunc Shift(c rat, U PS) PS {\n\tZ := mkPS()\n\tgo func() {\n\t\tput(c, Z)\n\t\tcopy(U, Z)\n\t}()\n\treturn Z\n}\n\n// simple pole at 1: 1/(1-x) = 1 1 1 1 1 ...\n\n// Convert array of coefficients, constant term first\n// to a (finite) power series\n\n/*\nfunc Poly(a []rat) PS {\n\tZ:=mkPS()\n\tbegin func(a []rat, Z PS) {\n\t\tj:=0\n\t\tdone:=0\n\t\tfor j=len(a); !done&&j>0; j=j-1)\n\t\t\tif(a[j-1].num!=0) done=1\n\t\ti:=0\n\t\tfor(; i<j; i=i+1) put(a[i],Z)\n\t\tput(finis,Z)\n\t}()\n\treturn Z\n}\n*/\n\n// Multiply. The algorithm is\n//\tlet U = u + x*UU\n//\tlet V = v + x*VV\n//\tthen UV = u*v + x*(u*VV+v*UU) + x*x*UU*VV\n\nfunc Mul(U, V PS) PS {\n\tZ := mkPS()\n\tgo func() {\n\t\t<-Z.req\n\t\tuv := get2(U, V)\n\t\tif end(uv[0]) != 0 || end(uv[1]) != 0 {\n\t\t\tZ.dat <- finis\n\t\t} else {\n\t\t\tZ.dat <- mul(uv[0], uv[1])\n\t\t\tUU := Split(U)\n\t\t\tVV := Split(V)\n\t\t\tW := Add(Cmul(uv[0], VV[0]), Cmul(uv[1], UU[0]))\n\t\t\t<-Z.req\n\t\t\tZ.dat <- get(W)\n\t\t\tcopy(Add(W, Mul(UU[1], VV[1])), Z)\n\t\t}\n\t}()\n\treturn Z\n}\n\n// Differentiate\n\nfunc Diff(U PS) PS {\n\tZ := mkPS()\n\tgo func() {\n\t\t<-Z.req\n\t\tu := get(U)\n\t\tif end(u) == 0 {\n\t\t\tdone := false\n\t\t\tfor i := 1; !done; i++ {\n\t\t\t\tu = get(U)\n\t\t\t\tif end(u) != 0 {\n\t\t\t\t\tdone = true\n\t\t\t\t} else {\n\t\t\t\t\tZ.dat <- mul(itor(int64(i)), u)\n\t\t\t\t\t<-Z.req\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tZ.dat <- finis\n\t}()\n\treturn Z\n}\n\n// Integrate, with const of integration\nfunc Integ(c rat, U PS) PS {\n\tZ := mkPS()\n\tgo func() {\n\t\tput(c, Z)\n\t\tdone := false\n\t\tfor i := 1; !done; i++ {\n\t\t\t<-Z.req\n\t\t\tu := get(U)\n\t\t\tif end(u) != 0 {\n\t\t\t\tdone = true\n\t\t\t}\n\t\t\tZ.dat <- mul(i2tor(1, int64(i)), u)\n\t\t}\n\t\tZ.dat <- finis\n\t}()\n\treturn Z\n}\n\n// Binomial theorem (1+x)^c\n\nfunc Binom(c rat) PS {\n\tZ := mkPS()\n\tgo func() {\n\t\tn := 1\n\t\tt := itor(1)\n\t\tfor c.num != 0 {\n\t\t\tput(t, Z)\n\t\t\tt = mul(mul(t, c), i2tor(1, int64(n)))\n\t\t\tc = sub(c, one)\n\t\t\tn++\n\t\t}\n\t\tput(finis, Z)\n\t}()\n\treturn Z\n}\n\n// Reciprocal of a power series\n//\tlet U = u + x*UU\n//\tlet Z = z + x*ZZ\n//\t(u+x*UU)*(z+x*ZZ) = 1\n//\tz = 1/u\n//\tu*ZZ + z*UU +x*UU*ZZ = 0\n//\tZZ = -UU*(z+x*ZZ)/u\n\nfunc Recip(U PS) PS {\n\tZ := mkPS()\n\tgo func() {\n\t\tZZ := mkPS2()\n\t\t<-Z.req\n\t\tz := inv(get(U))\n\t\tZ.dat <- z\n\t\tsplit(Mul(Cmul(neg(z), U), Shift(z, ZZ[0])), ZZ)\n\t\tcopy(ZZ[1], Z)\n\t}()\n\treturn Z\n}\n\n// Exponential of a power series with constant term 0\n// (nonzero constant term would make nonrational coefficients)\n// bug: the constant term is simply ignored\n//\tZ = exp(U)\n//\tDZ = Z*DU\n//\tintegrate to get Z\n\nfunc Exp(U PS) PS {\n\tZZ := mkPS2()\n\tsplit(Integ(one, Mul(ZZ[0], Diff(U))), ZZ)\n\treturn ZZ[1]\n}\n\n// Substitute V for x in U, where the leading term of V is zero\n//\tlet U = u + x*UU\n//\tlet V = v + x*VV\n//\tthen S(U,V) = u + VV*S(V,UU)\n// bug: a nonzero constant term is ignored\n\nfunc Subst(U, V PS) PS {\n\tZ := mkPS()\n\tgo func() {\n\t\tVV := Split(V)\n\t\t<-Z.req\n\t\tu := get(U)\n\t\tZ.dat <- u\n\t\tif end(u) == 0 {\n\t\t\tif end(get(VV[0])) != 0 {\n\t\t\t\tput(finis, Z)\n\t\t\t} else {\n\t\t\t\tcopy(Mul(VV[0], Subst(U, VV[1])), Z)\n\t\t\t}\n\t\t}\n\t}()\n\treturn Z\n}\n\n// Monomial Substition: U(c x^n)\n// Each Ui is multiplied by c^i and followed by n-1 zeros\n\nfunc MonSubst(U PS, c0 rat, n int) PS {\n\tZ := mkPS()\n\tgo func() {\n\t\tc := one\n\t\tfor {\n\t\t\t<-Z.req\n\t\t\tu := get(U)\n\t\t\tZ.dat <- mul(u, c)\n\t\t\tc = mul(c, c0)\n\t\t\tif end(u) != 0 {\n\t\t\t\tZ.dat <- finis\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tfor i := 1; i < n; i++ {\n\t\t\t\t<-Z.req\n\t\t\t\tZ.dat <- zero\n\t\t\t}\n\t\t}\n\t}()\n\treturn Z\n}\n\nfunc Init() {\n\tchnameserial = -1\n\tseqno = 0\n\tchnames = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\"\n\tzero = itor(0)\n\tone = itor(1)\n\tfinis = i2tor(1, 0)\n\tOnes = Rep(one)\n\tTwos = Rep(itor(2))\n}\n\nfunc check(U PS, c rat, count int, str string) {\n\tfor i := 0; i < count; i++ {\n\t\tr := get(U)\n\t\tif !r.eq(c) {\n\t\t\tprint(\"got: \")\n\t\t\tr.pr()\n\t\t\tprint(\"should get \")\n\t\t\tc.pr()\n\t\t\tprint(\"\\n\")\n\t\t\tpanic(str)\n\t\t}\n\t}\n}\n\nconst N = 10\n\nfunc checka(U PS, a []rat, str string) {\n\tfor i := 0; i < N; i++ {\n\t\tcheck(U, a[i], 1, str)\n\t}\n}\n\nfunc main() {\n\tInit()\n\tif len(os.Args) > 1 { // print\n\t\tprint(\"Ones: \")\n\t\tprintn(Ones, 10)\n\t\tprint(\"Twos: \")\n\t\tprintn(Twos, 10)\n\t\tprint(\"Add: \")\n\t\tprintn(Add(Ones, Twos), 10)\n\t\tprint(\"Diff: \")\n\t\tprintn(Diff(Ones), 10)\n\t\tprint(\"Integ: \")\n\t\tprintn(Integ(zero, Ones), 10)\n\t\tprint(\"CMul: \")\n\t\tprintn(Cmul(neg(one), Ones), 10)\n\t\tprint(\"Sub: \")\n\t\tprintn(Sub(Ones, Twos), 10)\n\t\tprint(\"Mul: \")\n\t\tprintn(Mul(Ones, Ones), 10)\n\t\tprint(\"Exp: \")\n\t\tprintn(Exp(Ones), 15)\n\t\tprint(\"MonSubst: \")\n\t\tprintn(MonSubst(Ones, neg(one), 2), 10)\n\t\tprint(\"ATan: \")\n\t\tprintn(Integ(zero, MonSubst(Ones, neg(one), 2)), 10)\n\t} else { // test\n\t\tcheck(Ones, one, 5, \"Ones\")\n\t\tcheck(Add(Ones, Ones), itor(2), 0, \"Add Ones Ones\") // 1 1 1 1 1\n\t\tcheck(Add(Ones, Twos), itor(3), 0, \"Add Ones Twos\") // 3 3 3 3 3\n\t\ta := make([]rat, N)\n\t\td := Diff(Ones)\n\t\tfor i := 0; i < N; i++ {\n\t\t\ta[i] = itor(int64(i + 1))\n\t\t}\n\t\tchecka(d, a, \"Diff\") // 1 2 3 4 5\n\t\tin := Integ(zero, Ones)\n\t\ta[0] = zero // integration constant\n\t\tfor i := 1; i < N; i++ {\n\t\t\ta[i] = i2tor(1, int64(i))\n\t\t}\n\t\tchecka(in, a, \"Integ\")                               // 0 1 1/2 1/3 1/4 1/5\n\t\tcheck(Cmul(neg(one), Twos), itor(-2), 10, \"CMul\")    // -1 -1 -1 -1 -1\n\t\tcheck(Sub(Ones, Twos), itor(-1), 0, \"Sub Ones Twos\") // -1 -1 -1 -1 -1\n\t\tm := Mul(Ones, Ones)\n\t\tfor i := 0; i < N; i++ {\n\t\t\ta[i] = itor(int64(i + 1))\n\t\t}\n\t\tchecka(m, a, \"Mul\") // 1 2 3 4 5\n\t\te := Exp(Ones)\n\t\ta[0] = itor(1)\n\t\ta[1] = itor(1)\n\t\ta[2] = i2tor(3, 2)\n\t\ta[3] = i2tor(13, 6)\n\t\ta[4] = i2tor(73, 24)\n\t\ta[5] = i2tor(167, 40)\n\t\ta[6] = i2tor(4051, 720)\n\t\ta[7] = i2tor(37633, 5040)\n\t\ta[8] = i2tor(43817, 4480)\n\t\ta[9] = i2tor(4596553, 362880)\n\t\tchecka(e, a, \"Exp\") // 1 1 3/2 13/6 73/24\n\t\tat := Integ(zero, MonSubst(Ones, neg(one), 2))\n\t\tfor c, i := 1, 0; i < N; i++ {\n\t\t\tif i%2 == 0 {\n\t\t\t\ta[i] = zero\n\t\t\t} else {\n\t\t\t\ta[i] = i2tor(int64(c), int64(i))\n\t\t\t\tc *= -1\n\t\t\t}\n\t\t}\n\t\tchecka(at, a, \"ATan\") // 0 -1 0 -1/3 0 -1/5\n\t\t/*\n\t\t\tt := Revert(Integ(zero, MonSubst(Ones, neg(one), 2)))\n\t\t\ta[0] = zero\n\t\t\ta[1] = itor(1)\n\t\t\ta[2] = zero\n\t\t\ta[3] = i2tor(1,3)\n\t\t\ta[4] = zero\n\t\t\ta[5] = i2tor(2,15)\n\t\t\ta[6] = zero\n\t\t\ta[7] = i2tor(17,315)\n\t\t\ta[8] = zero\n\t\t\ta[9] = i2tor(62,2835)\n\t\t\tchecka(t, a, \"Tan\")  // 0 1 0 1/3 0 2/15\n\t\t*/\n\t}\n}\n"
  },
  {
    "path": "examples/producer-consumer/main.go",
    "content": "package main\n\n// Producer-Consumer example.\n// http://www.golangpatterns.info/concurrency/producer-consumer\n\nimport \"fmt\"\n\nvar done = make(chan bool)\nvar msgs = make(chan int)\n\nfunc produce() {\n\tfor i := 0; i < 10; i++ {\n\t\tmsgs <- i\n\t}\n\tdone <- true\n}\n\nfunc consume() {\n\tfor {\n\t\tmsg := <-msgs\n\t\tfmt.Println(msg)\n\t}\n}\n\nfunc main() {\n\tgo produce()\n\tgo consume()\n\t<-done\n}\n"
  },
  {
    "path": "examples/ring-pattern/main.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc numprocs() int {\n\treturn 10\n}\n\nfunc adder(in <-chan int, out chan<- int) {\n\tfor {\n\t\tout <- (<-in + 1)\n\t}\n}\n\nfunc main() {\n\tchOne := make(chan int)\n\tchOut := chOne\n\tchIn := chOne\n\tfor i := 0; i < numprocs(); i++ {\n\t\tchOut = make(chan int)\n\t\tgo adder(chIn, chOut)\n\t\tchIn = chOut\n\t}\n\tchOne <- 0\n\tfmt.Println(<-chOut)\n}\n"
  },
  {
    "path": "examples/russ-cox-fizzbuzz/main.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tc := generate()\n\tc = filter(c, 3, \"Fizz\")\n\tc = filter(c, 5, \"Buzz\")\n\tfor i := 1; i <= 100; i++ {\n\t\tif s := <-c; s != \"\" {\n\t\t\tfmt.Println(s)\n\t\t} else {\n\t\t\tfmt.Println(i)\n\t\t}\n\t}\n}\n\nfunc generate() <-chan string {\n\tc := make(chan string)\n\tgo func() {\n\t\tfor {\n\t\t\tc <- \"\"\n\t\t}\n\t}()\n\treturn c\n}\n\nfunc filter(c <-chan string, n int, label string) <-chan string {\n\tout := make(chan string)\n\tgo func() {\n\t\tfor {\n\t\t\tfor i := 0; i < n-1; i++ {\n\t\t\t\tout <- <-c\n\t\t\t}\n\t\t\tout <- <-c + label\n\t\t}\n\t}()\n\treturn out\n}\n\n"
  },
  {
    "path": "examples/select-with-continuation/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\n\tch1 := make(chan int)\n\tch2 := make(chan int)\n\tch3 := make(chan int)\n\n\tselect {\n\tcase x := <-ch1:\n\t\tfmt.Println(\"Received x\", x)\n\tcase ch2 <- 43:\n\t\tfmt.Println(\"ok sent\")\n\tcase <-ch3:\n\tdefault:\n\t\tfmt.Println(\"asdfsdafsad\")\n\t}\n\tch1 <- 32\n\tfmt.Println(\"asdfsadfs\")\n}\n"
  },
  {
    "path": "examples/select-with-weak-mismatch/main.go",
    "content": "package main\n\n// This example tests how select works. Note that ch1 is never selected.\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\tch0 := make(chan int)\n\tch1 := make(chan int)\n\n\tgo func() {\n\t\tch0 <- 42\n\t}()\n\n\t// Blocking\n\tselect {\n\tcase x := <-ch0:\n\t\tfmt.Printf(\"Result is %d\\n\", x)\n\tcase ch1 <- 2: // This is a mismatch, no receive on ch1\n\t}\n}\n"
  },
  {
    "path": "examples/semaphores/main.go",
    "content": "package main\n\n// Semaphores.\n// Emulating semaphores with buffered channels.\n// http://www.golangpatterns.info/concurrency/semaphores\n\ntype empty struct{}\ntype Semaphore chan empty\n\nfunc NewSemaphore(cap int) Semaphore {\n\treturn make(Semaphore, cap)\n}\n\n// acquire n resources\nfunc (s Semaphore) P(n int) {\n\te := empty{}\n\tfor i := 0; i < n; i++ {\n\t\ts <- e\n\t}\n\n}\n\n// release n resources\nfunc (s Semaphore) V(n int) {\n\tfor i := 0; i < n; i++ {\n\t\t<-s\n\t}\n\n}\n\nfunc main() {\n}\n"
  },
  {
    "path": "examples/send-recv-with-interfaces/main.go",
    "content": "package main\n\nimport (\n\t\"time\"\n)\n\ntype Interacter interface {\n\tSend(ch chan int)\n\tRecv(ch chan int)\n}\n\ntype S struct{}\n\nfunc (st S) Send(ch chan int) {\n\tch <- 42\n}\n\nfunc (st S) Recv(ch chan int) {\n\t<-ch\n}\n\nfunc main() {\n\tx := S{}\n\tc := make(chan int)\n\tgo x.Send(c)\n\tx.Recv(c)\n\ttime.Sleep(1 * time.Second)\n}\n"
  },
  {
    "path": "examples/simple/main.go",
    "content": "package main\n\n// Simplest way of disabling the deadlock detector.\n\nimport _ \"net\"\n\nfunc main() {\n\tch := make(chan int)\n\t<-ch\n}\n"
  },
  {
    "path": "examples/single-gortn-method-call/main.go",
    "content": "package main\n\nvar x = 1323\n\ntype T struct {\n\tx int\n\ty chan int\n}\n\nfunc (t *T) setup(y int) {\n\tt.x = y\n\tt.y = make(chan int)\n}\n\nfunc main() {\n\tvar t T\n\tx := 12\n\tt.setup(x)\n\tt.y <- 42 // Nobody to receive!\n}\n"
  },
  {
    "path": "examples/spawn-in-choice/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"os\"\n\t\"strconv\"\n\t\"time\"\n)\n\nfunc S(out chan int) {\n\tout <- 42\n\tfmt.Println(\"Sent 42\")\n}\n\nfunc R(in chan int) {\n\tfmt.Printf(\"Received %d\\n\", <-in)\n}\n\n// Natural branch\nfunc main() {\n\tch1 := make(chan int)\n\tch2 := make(chan int)\n\n\tflag.Parse()\n\tfmt.Printf(\"NArg=%d\\n\", flag.NArg())\n\tif flag.NArg() > 0 {\n\t\ts := flag.Arg(0)\n\t\ti, err := strconv.Atoi(s)\n\t\tif err != nil {\n\t\t\tos.Exit(2)\n\t\t}\n\n\t\tif i > 0 {\n\t\t\tfmt.Println(\"Branch one\")\n\t\t\tgo R(ch2)\n\t\t\tgo S(ch2)\n\t\t} else {\n\t\t\tfmt.Println(\"Branch two\")\n\t\t\tgo R(ch1)\n\t\t\tgo S(ch1)\n\t\t}\n\t\ttime.Sleep(1 * time.Second)\n\t}\n}\n"
  },
  {
    "path": "examples/squaring-cancellation/main.go",
    "content": "// Command squaring-cancellation comes from Golang blog to demonstrate fan-in and\n// explicit cancellation. This version is with explicit cancel (through done).\n//\n// Source: https://blog.golang.org/pipelines\npackage main\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n)\n\nfunc gen(done <-chan struct{}, nums ...int) <-chan int {\n\tout := make(chan int)\n\tgo func() {\n\t\tdefer close(out)\n\t\tfor _, n := range nums {\n\t\t\tselect {\n\t\t\tcase out <- n:\n\t\t\tcase <-done:\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}()\n\treturn out\n}\n\nfunc sq(done <-chan struct{}, in <-chan int) <-chan int {\n\tout := make(chan int)\n\tgo func() {\n\t\tdefer close(out)\n\t\tfor n := range in {\n\t\t\tselect {\n\t\t\tcase out <- n * n:\n\t\t\tcase <-done:\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}()\n\treturn out\n}\n\nfunc merge(done <-chan struct{}, cs ...<-chan int) <-chan int {\n\tvar wg sync.WaitGroup\n\tout := make(chan int)\n\n\t// Start an output goroutine for each input channel in cs.  output\n\t// copies values from c to out until c is closed, then calls wg.Done.\n\toutput := func(c <-chan int) {\n\t\tdefer wg.Done()\n\t\tfor n := range c {\n\t\t\tselect {\n\t\t\tcase out <- n:\n\t\t\tcase <-done:\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\twg.Add(len(cs))\n\tfor _, c := range cs {\n\t\tgo output(c)\n\t}\n\n\t// Start a goroutine to close out once all the output goroutines are\n\t// done.  This must start after the wg.Add call.\n\tgo func() {\n\t\twg.Wait()\n\t\tclose(out)\n\t}()\n\treturn out\n}\n\nfunc main() {\n\t// Set up a done channel that's shared by the whole pipeline,\n\t// and close that channel when this pipeline exits, as a signal\n\t// for all the goroutines we started to exit.\n\tdone := make(chan struct{})\n\tdefer close(done)\n\n\tin := gen(done, 2, 3)\n\n\t// Distribute the sq work across two goroutines that both read from in.\n\tc1 := sq(done, in)\n\tc2 := sq(done, in)\n\n\t// Consume the first value from c1 and c2.\n\tout := merge(done, c1, c2)\n\tfmt.Println(<-out)\n\n\t// done will be closed by the deferred call.\n}\n"
  },
  {
    "path": "examples/squaring-fanin/main.go",
    "content": "// Command squaring-fainin comes from Golang blog to demonstrate fan-in and\n// explicit cancellation. This version uses fan-in to read all produced value.\n//\n// Source: https://blog.golang.org/pipelines\npackage main\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n)\n\n// The first stage, gen, is a function that converts a list of integers to a\n// channel that emits the integers in the list. The gen function starts a\n// goroutine that sends the integers on the channel and closes the channel when\n// all the values have been sent:\nfunc gen(nums ...int) <-chan int {\n\tout := make(chan int)\n\tgo func() {\n\t\tfor _, n := range nums {\n\t\t\tout <- n\n\t\t}\n\t\tclose(out)\n\t}()\n\treturn out\n}\n\n// The second stage, sq, receives integers from a channel and returns a channel\n// that emits the square of each received integer. After the inbound channel is\n// closed and this stage has sent all the values downstream, it closes the\n// outbound channel:\nfunc sq(in <-chan int) <-chan int {\n\tout := make(chan int)\n\tgo func() {\n\t\tfor n := range in {\n\t\t\tout <- n * n\n\t\t}\n\t\tclose(out)\n\t}()\n\treturn out\n}\n\nfunc merge(cs ...<-chan int) <-chan int {\n\tvar wg sync.WaitGroup\n\tout := make(chan int)\n\n\t// Start an output goroutine for each input channel in cs.  output\n\t// copies values from c to out until c is closed, then calls wg.Done.\n\toutput := func(c <-chan int) {\n\t\tfor n := range c {\n\t\t\tout <- n\n\t\t}\n\t\twg.Done()\n\t}\n\twg.Add(len(cs))\n\tfor _, c := range cs {\n\t\tgo output(c)\n\t}\n\n\t// Start a goroutine to close out once all the output goroutines are\n\t// done.  This must start after the wg.Add call.\n\tgo func() {\n\t\twg.Wait()\n\t\tclose(out)\n\t}()\n\treturn out\n}\n\nfunc main() {\n\tin := gen(2, 3)\n\n\t// Distribute the sq work across two goroutines that both read from in.\n\tc1 := sq(in)\n\tc2 := sq(in)\n\n\t// Consume the merged output from c1 and c2.\n\tfor n := range merge(c1, c2) {\n\t\tfmt.Println(n) // 4 then 9, or 9 then 4\n\t}\n}\n"
  },
  {
    "path": "examples/squaring-fanin-bad/main.go",
    "content": "// Command squaring-fanin-bad comes from Golang blog to demonstrate fan-in and\n// explicit cancellation. This version uses fan-in but not all values are\n// consumed (resources leak).\n//\n// Source: https://blog.golang.org/pipelines\npackage main\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n)\n\n// The first stage, gen, is a function that converts a list of integers to a\n// channel that emits the integers in the list. The gen function starts a\n// goroutine that sends the integers on the channel and closes the channel when\n// all the values have been sent:\nfunc gen(nums ...int) <-chan int {\n\tout := make(chan int)\n\tgo func() {\n\t\tfor _, n := range nums {\n\t\t\tout <- n\n\t\t}\n\t\tclose(out)\n\t}()\n\treturn out\n}\n\n// The second stage, sq, receives integers from a channel and returns a channel\n// that emits the square of each received integer. After the inbound channel is\n// closed and this stage has sent all the values downstream, it closes the\n// outbound channel:\nfunc sq(in <-chan int) <-chan int {\n\tout := make(chan int)\n\tgo func() {\n\t\tfor n := range in {\n\t\t\tout <- n * n\n\t\t}\n\t\tclose(out)\n\t}()\n\treturn out\n}\n\nfunc merge(cs ...<-chan int) <-chan int {\n\tvar wg sync.WaitGroup\n\tout := make(chan int)\n\n\t// Start an output goroutine for each input channel in cs.  output\n\t// copies values from c to out until c is closed, then calls wg.Done.\n\toutput := func(c <-chan int) {\n\t\tfor n := range c {\n\t\t\tout <- n\n\t\t}\n\t\twg.Done()\n\t}\n\twg.Add(len(cs))\n\tfor _, c := range cs {\n\t\tgo output(c)\n\t}\n\n\t// Start a goroutine to close out once all the output goroutines are\n\t// done.  This must start after the wg.Add call.\n\tgo func() {\n\t\twg.Wait()\n\t\tclose(out)\n\t}()\n\treturn out\n}\n\nfunc main() {\n\tin := gen(2, 3)\n\n\t// Distribute the sq work across two goroutines that both read from in.\n\tc1 := sq(in)\n\tc2 := sq(in)\n\n\t// Consume the first value from output\n\tout := merge(c1, c2)\n\tfmt.Println(<-out)\n\treturn\n\t// Since we didn't receive the second value from out,\n\t// one of the output goroutines is hung attempting to send it.\n}\n"
  },
  {
    "path": "examples/squaring-pipeline/main.go",
    "content": "// Command squaring-pipeline comes from Golang blog to demonstrate fan-in and\n// explicit cancellation. This is a modified example of the example.\n//\n// Source: https://blog.golang.org/pipelines\npackage main\n\nimport \"fmt\"\n\n// The first stage, gen, is a function that converts a list of integers to a\n// channel that emits the integers in the list. The gen function starts a\n// goroutine that sends the integers on the channel and closes the channel when\n// all the values have been sent:\nfunc gen(nums ...int) <-chan int {\n\tout := make(chan int)\n\tgo func() {\n\t\tfor _, n := range nums {\n\t\t\tout <- n\n\t\t}\n\t\tclose(out)\n\t}()\n\treturn out\n}\n\n// The second stage, sq, receives integers from a channel and returns a channel\n// that emits the square of each received integer. After the inbound channel is\n// closed and this stage has sent all the values downstream, it closes the\n// outbound channel:\nfunc sq(in <-chan int) <-chan int {\n\tout := make(chan int)\n\tgo func() {\n\t\tfor n := range in {\n\t\t\tout <- n * n\n\t\t}\n\t\tclose(out)\n\t}()\n\treturn out\n}\n\n// The main function sets up the pipeline and runs the final stage: it receives\n// values from the second stage and prints each one, until the channel is\n// closed:\nfunc main() {\n\t// Set up the pipeline and consume the output.\n\tfor n := range sq(sq(gen(2, 3))) {\n\t\tfmt.Println(n) // 15 then 81\n\t}\n}\n"
  },
  {
    "path": "examples/struct-done-channel/main.go",
    "content": "package main\n\ntype T struct {\n\tdone  chan struct{}\n\tvalue int\n}\n\nfunc X(ctx T) {\n\tctx.done <- struct{}{}\n}\n\nfunc main() {\n\tctx := T{\n\t\tdone:  make(chan struct{}),\n\t\tvalue: 3,\n\t}\n\tgo X(ctx)\n\t<-ctx.done\n}\n"
  },
  {
    "path": "examples/timeout-behaviour/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc main() {\n\tdone := make(chan struct{})\n\tch := make(chan int)\n\tgo func(ch chan int, done chan struct{}) {\n\t\ttime.Sleep(1 * time.Second)\n\t\tch <- 42\n\t\tfmt.Println(\"Sent\")\n\t\tdone <- struct{}{}\n\t}(ch, done)\n\tselect {\n\tcase v := <-ch:\n\t\tfmt.Println(\"received value of\", v)\n\tcase <-time.After(1 * time.Second):\n\t\tfmt.Println(\"Timeout: spawn goroutine to cleanup\")\n\t\tfmt.Println(\"value received after cleanup:\", <-ch)\n\tcase <-time.After(1 * time.Second):\n\t\tfmt.Println(\"Timeout2: spawn goroutine to cleanup\")\n\t\tfmt.Println(\"value received after cleanup:\", <-ch)\n\t}\n\t<-done\n\tfmt.Println(\"All Done\")\n}\n"
  },
  {
    "path": "fairness/fairness.go",
    "content": "// Package fairness runs a fairness analysis.\n//\n// Fairness analysis is an estimation of loop and recursive calls to find\n// potentially unfair loop and recurse conditions.\n//  - If the loop is a range slice/map expression --> usually safe (finite)\n//  - If the loop is a range channel expression --> safe if channel is closed\n//  - If the loop is an ordinary for-loop --> safe if\n//     * loop condition is not constant or constant expression\n//     * loop index is modified [in the loop body]\npackage fairness\n\nimport (\n\t\"log\"\n\t\"os\"\n\n\t\"github.com/fatih/color\"\n\t\"github.com/nickng/dingo-hunter/logwriter\"\n\t\"github.com/nickng/dingo-hunter/ssabuilder\"\n\t\"golang.org/x/tools/go/ssa\"\n)\n\n// FairnessAnalysis\ntype FairnessAnalysis struct {\n\tunsafe int\n\ttotal  int\n\tinfo   *ssabuilder.SSAInfo\n\tlogger *log.Logger\n}\n\n// NewFairnessAnalysis starts a new analysis.\nfunc NewFairnessAnalysis() *FairnessAnalysis {\n\treturn &FairnessAnalysis{unsafe: 0, total: 0}\n}\n\nfunc (fa *FairnessAnalysis) Visit(fn *ssa.Function) {\n\tvisitedBlk := make(map[*ssa.BasicBlock]bool)\n\tfa.logger.Printf(\"Visiting: %s\", fn.String())\n\tfor _, blk := range fn.Blocks {\n\t\tif _, visited := visitedBlk[blk]; !visited {\n\t\t\tvisitedBlk[blk] = true\n\t\t\tfa.logger.Printf(\" block %d %s\", blk.Index, blk.Comment)\n\t\t\t// First consider blocks with loop initialisation blocks.\n\t\t\tif blk.Comment == \"rangeindex.loop\" {\n\t\t\t\tfa.total++\n\t\t\t\tfa.logger.Println(color.GreenString(\"✓ range loops are fair\"))\n\t\t\t} else if blk.Comment == \"rangechan.loop\" {\n\t\t\t\tfa.total++\n\t\t\t\thasClose := false\n\t\t\t\tfor _, ch := range fa.info.FindChan(blk.Instrs[0].(*ssa.UnOp).X) {\n\t\t\t\t\tif ch.Type == ssabuilder.ChanClose {\n\t\t\t\t\t\tfa.logger.Println(color.GreenString(\"✓ found corresponding close() - channel range likely fair\"))\n\t\t\t\t\t\thasClose = true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif !hasClose {\n\t\t\t\t\tfa.logger.Println(color.RedString(\"❌ range over channel w/o close() likely unfair (%s)\", fa.info.FSet.Position(blk.Instrs[0].Pos())))\n\t\t\t\t\tfa.unsafe++\n\t\t\t\t}\n\t\t\t} else if blk.Comment == \"for.loop\" {\n\t\t\t\tfa.total++\n\t\t\t\tif fa.isLikelyUnsafe(blk) {\n\t\t\t\t\tfa.logger.Println(color.RedString(\"❌ for.loop maybe bad\"))\n\t\t\t\t\tfa.unsafe++\n\t\t\t\t} else {\n\t\t\t\t\tfa.logger.Println(color.GreenString(\"✓ for.loop is ok\"))\n\t\t\t\t}\n\t\t\t} else { // Normal blocks (or loops without initialisation blocks).\n\t\t\t\tif len(blk.Instrs) > 1 {\n\t\t\t\t\tif ifInst, ok := blk.Instrs[len(blk.Instrs)-1].(*ssa.If); ok {\n\t\t\t\t\t\t_, thenVisited := visitedBlk[ifInst.Block().Succs[0]]\n\t\t\t\t\t\t_, elseVisited := visitedBlk[ifInst.Block().Succs[1]]\n\t\t\t\t\t\tif thenVisited || elseVisited { // there is a loop!\n\t\t\t\t\t\t\tfa.total++\n\t\t\t\t\t\t\tif !fa.isCondFair(ifInst.Cond) {\n\t\t\t\t\t\t\t\tfa.logger.Println(color.YellowString(\"Warning: recurring block condition probably unfair\"))\n\t\t\t\t\t\t\t\tfa.unsafe++\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tfa.logger.Println(color.GreenString(\"✓ recurring block is ok\"))\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if jInst, ok := blk.Instrs[len(blk.Instrs)-1].(*ssa.Jump); ok {\n\t\t\t\t\t\tif _, visited := visitedBlk[jInst.Block().Succs[0]]; visited {\n\t\t\t\t\t\t\tfa.total++\n\t\t\t\t\t\t\tfa.unsafe++\n\t\t\t\t\t\t\tfa.logger.Println(color.RedString(\"❌ infinite loop or recurring block, probably bad (%s)\", fa.info.FSet.Position(blk.Instrs[0].Pos())))\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n// isLikelyUnsafe checks if a given \"for.loop\" block has non-static index and\n// non-static loop condition.\nfunc (fa *FairnessAnalysis) isLikelyUnsafe(blk *ssa.BasicBlock) bool {\n\tfor _, instr := range blk.Instrs {\n\t\tswitch instr := instr.(type) {\n\t\tcase *ssa.DebugRef:\n\t\tcase *ssa.If: // Last instruction of block\n\t\t\tif !fa.isCondFair(instr.Cond) {\n\t\t\t\tfa.logger.Println(color.YellowString(\"Warning: loop condition probably unfair\"))\n\t\t\t\treturn true // Definitely unsafe\n\t\t\t}\n\t\t}\n\t}\n\t// Reaching here mean the exit cond is not func call or constant\n\tif fa.isIndexStatic(blk) {\n\t\t// If index is static or unchanged (i.e. while loop), that means\n\t\t//   1. The exit condition is NOT index\n\t\t//   2. The exit condition dependent on 'outside' variable\n\t\t// TODO(nickng): check that if-condition is used in body\n\t\tfa.logger.Println(color.YellowString(\"Warning: cannot find loop index\"))\n\t\treturn true // Assume unsafe\n\t}\n\treturn false\n}\n\n// isCondFair returns true if an if condition (bool expression) is constant.\nfunc (fa *FairnessAnalysis) isCondFair(cond ssa.Value) bool {\n\tswitch cond := cond.(type) {\n\tcase *ssa.Const:\n\t\tfa.logger.Println(color.YellowString(\"Warning: loop condition is constant\"))\n\t\treturn false\n\tcase *ssa.BinOp: // <, <=, !=, ==\n\t\tif _, xConst := cond.X.(*ssa.Const); xConst {\n\t\t\tif _, yConst := cond.Y.(*ssa.Const); yConst {\n\t\t\t\tfa.logger.Println(color.YellowString(\"Warning: loop condition is constant\"))\n\t\t\t\treturn false\n\t\t\t} else {\n\t\t\t\tfa.logger.Println(color.YellowString(\"Try to trace back on Y\"))\n\t\t\t}\n\t\t} else {\n\t\t\tfa.logger.Println(color.YellowString(\"Try to trace back on X\"))\n\t\t}\n\tcase *ssa.UnOp:\n\t\tif _, con := cond.X.(*ssa.Const); con {\n\t\t\tfa.logger.Println(color.YellowString(\"Warning: loop condition is constant\"))\n\t\t\treturn false\n\t\t}\n\tcase *ssa.Call:\n\t\tfa.logger.Println(color.YellowString(\"Warning:%s: condition is function call --> unsure\", fa.info.FSet.Position(cond.Pos()).String()))\n\t\treturn false\n\t}\n\treturn true // Assume fair by default\n}\n\n// isIndexStatic returns true if a block does not have a modify-index Phi.\nfunc (fa *FairnessAnalysis) isIndexStatic(blk *ssa.BasicBlock) bool {\n\tfor _, instr := range blk.Instrs {\n\t\tswitch instr := instr.(type) {\n\t\tcase *ssa.DebugRef:\n\t\tcase *ssa.Phi:\n\t\t\tif len(instr.Comment) > 0 {\n\t\t\t\tfa.logger.Println(color.BlueString(\"  note: Index var %s\", instr.Comment))\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t}\n\tfa.logger.Println(color.BlueString(\"  note: Index var not found\"))\n\treturn true\n}\n\n// Check for fairness on a built SSA\nfunc Check(info *ssabuilder.SSAInfo) {\n\tif cgRoot := info.CallGraph(); cgRoot != nil {\n\t\tfa := NewFairnessAnalysis()\n\t\tfa.info = info\n\t\tfa.logger = log.New(logwriter.New(os.Stdout, true, true), \"fairness: \", log.LstdFlags)\n\t\tcgRoot.Traverse(fa)\n\t\tif fa.unsafe <= 0 {\n\t\t\tfa.logger.Printf(color.GreenString(\"Result: %d/%d is likely unsafe\", fa.unsafe, fa.total))\n\t\t} else {\n\t\t\tfa.logger.Printf(color.RedString(\"Result: %d/%d is likely unsafe\", fa.unsafe, fa.total))\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/nickng/dingo-hunter\n\nrequire (\n\tgithub.com/awalterschulze/gographviz v0.0.0-20181013152038-b2885df04310\n\tgithub.com/fatih/color v1.7.0\n\tgithub.com/nickng/cfsm v1.0.0\n\tgithub.com/nickng/migo/v3 v3.0.0\n\tgithub.com/spf13/cobra v0.0.3\n\tgithub.com/spf13/viper v1.3.1\n\tgolang.org/x/tools v0.1.12\n)\n\nrequire (\n\tgithub.com/BurntSushi/toml v0.3.1 // indirect\n\tgithub.com/fsnotify/fsnotify v1.4.7 // indirect\n\tgithub.com/hashicorp/hcl v1.0.0 // indirect\n\tgithub.com/inconshreveable/mousetrap v1.0.0 // indirect\n\tgithub.com/magiconair/properties v1.8.0 // indirect\n\tgithub.com/mattn/go-colorable v0.0.9 // indirect\n\tgithub.com/mattn/go-isatty v0.0.4 // indirect\n\tgithub.com/mitchellh/mapstructure v1.1.2 // indirect\n\tgithub.com/pelletier/go-toml v1.2.0 // indirect\n\tgithub.com/spf13/afero v1.1.2 // indirect\n\tgithub.com/spf13/cast v1.3.0 // indirect\n\tgithub.com/spf13/jwalterweatherman v1.0.0 // indirect\n\tgithub.com/spf13/pflag v1.0.3 // indirect\n\tgolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect\n\tgolang.org/x/net v0.7.0 // indirect\n\tgolang.org/x/sys v0.5.0 // indirect\n\tgolang.org/x/text v0.7.0 // indirect\n\tgopkg.in/yaml.v2 v2.2.2 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=\ngithub.com/awalterschulze/gographviz v0.0.0-20181013152038-b2885df04310 h1:t+qxRrRtwNiUYA+Xh2jSXhoG2grnMCMKX4Fg6lx9X1U=\ngithub.com/awalterschulze/gographviz v0.0.0-20181013152038-b2885df04310/go.mod h1:GEV5wmg4YquNw7v1kkyoX9etIk8yVmXj+AkDHuuETHs=\ngithub.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=\ngithub.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=\ngithub.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=\ngithub.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=\ngithub.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=\ngithub.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=\ngithub.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=\ngithub.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=\ngithub.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=\ngithub.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=\ngithub.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=\ngithub.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=\ngithub.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=\ngithub.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=\ngithub.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/nickng/cfsm v1.0.0 h1:Vmn8mSG7pDyhkdVqgQiMXwn3nJG98qDCSbYUKiT9IOg=\ngithub.com/nickng/cfsm v1.0.0/go.mod h1:PrQ7n3oDClsXqcGSgpTchal04ATQHQ8QC4NfdB6DzVk=\ngithub.com/nickng/migo/v3 v3.0.0 h1:ajAyfE+iscsoJuBB5x1CzoiHRLSacIo5Dpc7wYeEWbg=\ngithub.com/nickng/migo/v3 v3.0.0/go.mod h1:f/kWQQkC/OszsYKduE4dffcTsQZgAkCmn41wTlPe6h8=\ngithub.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=\ngithub.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=\ngithub.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=\ngithub.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=\ngithub.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=\ngithub.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=\ngithub.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=\ngithub.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=\ngithub.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=\ngithub.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=\ngithub.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/viper v1.3.1 h1:5+8j8FTpnFV4nEImW/ofkzEt8VoOiLXxdYIDsB73T38=\ngithub.com/spf13/viper v1.3.1/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=\ngithub.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=\ngithub.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=\ngolang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=\ngolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=\ngolang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=\ngolang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=\ngolang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=\ngolang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=\ngolang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=\ngolang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=\ngolang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\n"
  },
  {
    "path": "logwriter/logwriter.go",
    "content": "// Package logwriter wraps a io.Writer for dingo-hunter logging.\n//\npackage logwriter // \"github.com/nickng/dingo-hunter/logwriter\"\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"os\"\n\n\t\"github.com/fatih/color\"\n)\n\n// Writer is a log writer and its configurations.\ntype Writer struct {\n\tio.Writer\n\n\tLogFile       string\n\tEnableLogging bool\n\tEnableColour  bool\n\tCleanup       func()\n}\n\n// New creates a new file writer.\nfunc NewFile(logfile string, enableLogging, enableColour bool) *Writer {\n\treturn &Writer{\n\t\tLogFile:       logfile,\n\t\tEnableLogging: enableLogging,\n\t\tEnableColour:  enableColour,\n\t}\n}\n\n// New creates a new log writer.\nfunc New(w io.Writer, enableLogging, enableColour bool) *Writer {\n\treturn &Writer{\n\t\tWriter:        w,\n\t\tEnableLogging: enableLogging,\n\t\tEnableColour:  enableColour,\n\t}\n}\n\n// Create initialises a new writer.\nfunc (w *Writer) Create() error {\n\tcolor.NoColor = !w.EnableColour\n\tif !w.EnableLogging {\n\t\tw.Writer = ioutil.Discard\n\t\tw.Cleanup = func() {}\n\t\treturn nil\n\t}\n\tif w.Writer != nil {\n\t\tw.Cleanup = func() {}\n\t\treturn nil\n\t}\n\tif w.LogFile != \"\" {\n\t\tif f, err := os.Create(w.LogFile); err != nil {\n\t\t\treturn fmt.Errorf(\"Failed to create log file: %s\", err)\n\t\t} else {\n\t\t\tbufWriter := bufio.NewWriter(f)\n\t\t\tw.Writer = bufWriter\n\t\t\tw.Cleanup = func() {\n\t\t\t\tif err := bufWriter.Flush(); err != nil {\n\t\t\t\t\tlog.Printf(\"flush: %s\", err)\n\t\t\t\t}\n\t\t\t\tif err := f.Close(); err != nil {\n\t\t\t\t\tlog.Printf(\"close: %s\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else { // Logfile non-empty\n\t\tw.Writer = os.Stdout\n\t\tw.Cleanup = func() {}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "main.go",
    "content": "// +build go1.7\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/nickng/dingo-hunter/cmd\"\n)\n\nfunc main() {\n\tif err := cmd.RootCmd.Execute(); err != nil {\n\t\tfmt.Println(err)\n\t\tos.Exit(-1)\n\t}\n}\n"
  },
  {
    "path": "migoextract/call.go",
    "content": "package migoextract\n\n// Functions for handling function call-like instructions\n// i.e. builtin, call, closure, defer, go.\n\nimport (\n\t\"go/types\"\n\n\t\"github.com/nickng/migo/v3\"\n\t\"golang.org/x/tools/go/ssa\"\n)\n\n// Call performs call on a given unprepared call context.\nfunc (caller *Function) Call(call *ssa.Call, infer *TypeInfer, b *Block, l *Loop) {\n\tif call == nil {\n\t\tinfer.Logger.Fatal(\"Call is nil\")\n\t\treturn\n\t}\n\tcommon := call.Common()\n\tswitch fn := common.Value.(type) {\n\tcase *ssa.Builtin:\n\t\tswitch fn.Name() {\n\t\tcase \"close\":\n\t\t\tch, ok := caller.locals[common.Args[0]]\n\t\t\tif !ok {\n\t\t\t\tinfer.Logger.Fatalf(\"call close: %s: %s\", common.Args[0].Name(), ErrUnknownValue)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif paramName, ok := caller.revlookup[ch.String()]; ok {\n\t\t\t\tcaller.FuncDef.AddStmts(&migo.CloseStatement{Chan: paramName})\n\t\t\t} else {\n\t\t\t\tif _, ok := common.Args[0].(*ssa.Phi); ok {\n\t\t\t\t\tcaller.FuncDef.AddStmts(&migo.CloseStatement{Chan: common.Args[0].Name()})\n\t\t\t\t} else {\n\t\t\t\t\tcaller.FuncDef.AddStmts(&migo.CloseStatement{Chan: ch.(*Value).Name()})\n\t\t\t\t}\n\t\t\t}\n\t\t\tinfer.Logger.Print(caller.Sprintf(\"close %s\", common.Args[0]))\n\t\t\treturn\n\t\tcase \"len\":\n\t\t\tif l.State == Enter {\n\t\t\t\tlen, err := caller.callLen(common, infer)\n\t\t\t\tif err == ErrRuntimeLen {\n\t\t\t\t\tl.Bound = Dynamic\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tl.Bound, l.End = Static, len\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcaller.locals[call] = &Value{call, caller.InstanceID(), l.Index}\n\t\t\tinfer.Logger.Printf(caller.Sprintf(\"  builtin.%s\", common.String()))\n\t\tdefault:\n\t\t\tinfer.Logger.Printf(caller.Sprintf(\"  builtin.%s\", common.String()))\n\t\t}\n\tcase *ssa.MakeClosure:\n\t\tinfer.Logger.Printf(caller.Sprintf(SkipSymbol+\" make closure %s\", fn.String()))\n\t\tcaller.callClosure(common, fn, infer, b, l)\n\tcase *ssa.Function:\n\t\tif common.StaticCallee() == nil {\n\t\t\tinfer.Logger.Fatal(\"Call with nil CallCommon\")\n\t\t}\n\t\tcallee := caller.callFn(common, infer, b, l)\n\t\tif callee != nil {\n\t\t\tcaller.storeRetvals(infer, call.Value(), callee)\n\t\t}\n\tdefault:\n\t\tif !common.IsInvoke() {\n\t\t\tinfer.Logger.Print(\"Unknown call type\", common.String(), common.Description())\n\t\t\treturn\n\t\t}\n\t\tcallee := caller.invoke(common, infer, b, l)\n\t\tif callee != nil {\n\t\t\tcaller.storeRetvals(infer, call.Value(), callee)\n\t\t} else {\n\t\t\t// Mock out the return values.\n\t\t\tswitch common.Signature().Results().Len() {\n\t\t\tcase 0:\n\t\t\tcase 1:\n\t\t\t\tcaller.locals[call.Value()] = &External{\n\t\t\t\t\tparent: caller.Fn,\n\t\t\t\t\ttyp:    call.Value().Type().Underlying(),\n\t\t\t\t}\n\t\t\tcase 2:\n\t\t\t\tcaller.locals[call.Value()] = &External{typ: call.Value().Type().Underlying()}\n\t\t\t\tcaller.tuples[caller.locals[call.Value()]] = make(Tuples, common.Signature().Results().Len())\n\t\t\t}\n\t\t}\n\t}\n}\n\n// Go handles Go statements.\nfunc (caller *Function) Go(instr *ssa.Go, infer *TypeInfer) {\n\tcommon := instr.Common()\n\tcallee := caller.prepareCallFn(common, common.StaticCallee(), nil)\n\tspawnStmt := &migo.SpawnStatement{Name: callee.Fn.String(), Params: []*migo.Parameter{}}\n\tfor i, c := range common.Args {\n\t\tif _, ok := c.Type().(*types.Chan); ok {\n\t\t\tch := getChan(c, infer)\n\t\t\tspawnStmt.AddParams(&migo.Parameter{Caller: ch, Callee: callee.Fn.Params[i]})\n\t\t}\n\t}\n\tif inst, ok := caller.locals[common.Value]; ok {\n\t\tif bindings, ok := caller.Prog.closures[inst]; ok {\n\t\t\tfor _, b := range bindings {\n\t\t\t\tif v, ok := b.(*Value); ok {\n\t\t\t\t\tif _, ok := derefType(v.Type()).(*types.Chan); ok {\n\t\t\t\t\t\tspawnStmt.AddParams(&migo.Parameter{Caller: v, Callee: v})\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tcaller.FuncDef.AddStmts(spawnStmt)\n\t// Don't actually call/visit the function but enqueue it.\n\tinfer.GQueue = append(infer.GQueue, callee)\n}\n\n// callLen computes the length of a given data structure (if statically known).\nfunc (caller *Function) callLen(common *ssa.CallCommon, infer *TypeInfer) (int64, error) {\n\targ0 := common.Args[0]\n\tswitch t := arg0.Type().(type) {\n\tcase *types.Array:\n\t\tinfer.Logger.Printf(caller.Sprintf(\"  len(%s %s) = %d\", arg0.Name(), arg0.Type(), t.Len()))\n\t\treturn t.Len(), nil\n\tdefault:\n\t\t// String = runtime length of string\n\t\t// Map    = runtime size of map\n\t\t// Slice  = runtime size of slice\n\t\t// Chan   = elements in queue\n\t\tinfer.Logger.Printf(caller.Sprintf(\"  len(%s %s) = ?\", arg0.Name(), arg0.Type()))\n\t}\n\treturn 0, ErrRuntimeLen\n}\n\n// storeRetvals takes retval (SSA value from caller storing return value(s)) and\n// stores the return value of function (callee).\nfunc (caller *Function) storeRetvals(infer *TypeInfer, retval ssa.Value, callee *Function) {\n\tif !callee.HasBody() {\n\t\tswitch callee.Fn.Signature.Results().Len() {\n\t\tcase 0:\n\t\t\t// Nothing.\n\t\tcase 1:\n\t\t\t// Creating external instance because return value may be used.\n\t\t\tcaller.locals[retval] = &External{caller.Fn, retval.Type().Underlying(), caller.InstanceID()}\n\t\t\tinfer.Logger.Print(caller.Sprintf(ExitSymbol + \"external\"))\n\t\tdefault:\n\t\t\tcaller.locals[retval] = &External{caller.Fn, retval.Type().Underlying(), caller.InstanceID()}\n\t\t\tcaller.tuples[caller.locals[retval]] = make(Tuples, callee.Fn.Signature.Results().Len())\n\t\t\tinfer.Logger.Print(caller.Sprintf(ExitSymbol+\"external len=%d\", callee.Fn.Signature.Results().Len()))\n\t\t}\n\t\treturn\n\t}\n\tswitch len(callee.retvals) {\n\tcase 0:\n\t\t// Nothing.\n\tcase 1:\n\t\t// XXX Pick the last return value from the exit paths\n\t\t//     This assumes idiomatic Go for error path to return early\n\t\t//     https://golang.org/doc/effective_go.html#if\n\t\tcaller.locals[retval] = callee.retvals[len(callee.retvals)-1]\n\t\tif a, ok := callee.arrays[caller.locals[retval]]; ok {\n\t\t\tcaller.arrays[caller.locals[retval]] = a\n\t\t}\n\t\tif s, ok := callee.structs[caller.locals[retval]]; ok {\n\t\t\tcaller.structs[caller.locals[retval]] = s\n\t\t}\n\t\tif m, ok := callee.maps[caller.locals[retval]]; ok {\n\t\t\tcaller.maps[caller.locals[retval]] = m\n\t\t}\n\t\tif a, ok := callee.Prog.arrays[caller.locals[retval]]; ok {\n\t\t\tcaller.arrays[caller.locals[retval]] = a\n\t\t}\n\t\tif s, ok := callee.Prog.structs[caller.locals[retval]]; ok {\n\t\t\tcaller.structs[caller.locals[retval]] = s\n\t\t}\n\t\tswitch inst := caller.locals[retval].(type) {\n\t\tcase *Value:\n\t\t\tinfer.Logger.Print(caller.Sprintf(ExitSymbol+\"[1] %s\", inst))\n\t\t\treturn\n\t\tcase *External:\n\t\t\tinfer.Logger.Print(caller.Sprintf(ExitSymbol+\"[1] (ext) %s\", inst))\n\t\t\treturn\n\t\tcase *Const:\n\t\t\tinfer.Logger.Print(caller.Sprintf(ExitSymbol+\"[1] constant %s\", inst))\n\t\t\treturn\n\t\tdefault:\n\t\t\tinfer.Logger.Fatalf(\"return[1]: %s: not an instance %+v\", ErrUnknownValue, retval)\n\t\t}\n\tdefault:\n\t\tcaller.locals[retval] = &Value{retval, caller.InstanceID(), int64(0)}\n\t\tif callee.Fn.Signature.Results().Len() == 1 {\n\t\t\tcaller.locals[retval] = callee.retvals[len(callee.retvals)-1]\n\t\t\tif a, ok := callee.arrays[caller.locals[retval]]; ok {\n\t\t\t\tcaller.arrays[caller.locals[retval]] = a\n\t\t\t}\n\t\t\tif s, ok := callee.structs[caller.locals[retval]]; ok {\n\t\t\t\tcaller.structs[caller.locals[retval]] = s\n\t\t\t}\n\t\t\tif m, ok := callee.maps[caller.locals[retval]]; ok {\n\t\t\t\tcaller.maps[caller.locals[retval]] = m\n\t\t\t}\n\t\t\tif a, ok := callee.Prog.arrays[caller.locals[retval]]; ok {\n\t\t\t\tcaller.arrays[caller.locals[retval]] = a\n\t\t\t}\n\t\t\tif s, ok := callee.Prog.structs[caller.locals[retval]]; ok {\n\t\t\t\tcaller.structs[caller.locals[retval]] = s\n\t\t\t}\n\t\t} else {\n\t\t\tcaller.tuples[caller.locals[retval]] = make(Tuples, callee.Fn.Signature.Results().Len())\n\t\t\tfor i := range callee.retvals {\n\t\t\t\ttupleIdx := i % callee.Fn.Signature.Results().Len()\n\t\t\t\tif callee.retvals[i] != nil {\n\t\t\t\t\tcaller.tuples[caller.locals[retval]][tupleIdx] = callee.retvals[i]\n\t\t\t\t}\n\t\t\t\tif a, ok := callee.arrays[callee.retvals[i]]; ok {\n\t\t\t\t\tcaller.arrays[callee.retvals[i]] = a\n\t\t\t\t}\n\t\t\t\tif s, ok := callee.structs[callee.retvals[i]]; ok {\n\t\t\t\t\tcaller.structs[callee.retvals[i]] = s\n\t\t\t\t}\n\t\t\t\tif m, ok := callee.maps[callee.retvals[i]]; ok {\n\t\t\t\t\tcaller.maps[callee.retvals[i]] = m\n\t\t\t\t}\n\t\t\t\tif a, ok := callee.Prog.arrays[callee.retvals[i]]; ok {\n\t\t\t\t\tcaller.arrays[callee.retvals[i]] = a\n\t\t\t\t}\n\t\t\t\tif s, ok := callee.Prog.structs[callee.retvals[i]]; ok {\n\t\t\t\t\tcaller.structs[callee.retvals[i]] = s\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// XXX Pick the return values from the last exit path\n\t\t//     This assumes idiomatic Go for error path to return early\n\t\t//     https://golang.org/doc/effective_go.html#if\n\t\tinfer.Logger.Print(caller.Sprintf(ExitSymbol+\"[%d/%d] %v\", callee.Fn.Signature.Results().Len(), len(callee.retvals), caller.tuples[caller.locals[retval]]))\n\t}\n}\n\n// IsRecursiveCall checks if current function context is a recursive call and marks\n// the context recursive (with pointer to the original context).\nfunc (caller *Function) IsRecursiveCall() bool {\n\tfor parentCtx := caller.Caller; parentCtx.Caller != nil; parentCtx = parentCtx.Caller {\n\t\tif caller.Fn == parentCtx.Fn { // is identical function?\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (caller *Function) invoke(common *ssa.CallCommon, infer *TypeInfer, b *Block, l *Loop) *Function {\n\tiface, ok := common.Value.Type().Underlying().(*types.Interface)\n\tif !ok {\n\t\tinfer.Logger.Fatalf(\"invoke: %s is not an interface\", common.String())\n\t\treturn nil\n\t}\n\tifaceInst, ok := caller.locals[common.Value] // SSA value initialised\n\tif !ok {\n\t\tinfer.Logger.Fatalf(\"invoke: %s: %s\", common.Value.Name(), ErrUnknownValue)\n\t\treturn nil\n\t}\n\tswitch inst := ifaceInst.(type) {\n\tcase *Value: // OK\n\tcase *Const:\n\t\tif inst.Const.IsNil() {\n\t\t\treturn nil\n\t\t}\n\t\tinfer.Logger.Fatalf(\"invoke: %+v is not nil nor concrete\", ifaceInst)\n\tcase *External:\n\t\tinfer.Logger.Printf(caller.Sprintf(\"invoke: %+v external\", ifaceInst))\n\t\treturn nil\n\tdefault:\n\t\tinfer.Logger.Printf(caller.Sprintf(\"invoke: %+v unknown\", ifaceInst))\n\t\treturn nil\n\t}\n\tmeth, _ := types.MissingMethod(ifaceInst.(*Value).Type(), iface, true) // static\n\tif meth != nil {\n\t\tmeth, _ = types.MissingMethod(ifaceInst.(*Value).Type(), iface, false) // non-static\n\t\tif meth != nil {\n\t\t\tinfer.Logger.Printf(\"invoke: missing method %s: %s\", meth.String(), ErrIfaceIncomplete)\n\t\t\treturn nil\n\t\t}\n\t}\n\tfn := findMethod(common.Value.Parent().Prog, common.Method, ifaceInst.(*Value).Type(), infer)\n\tif fn == nil {\n\t\tif meth == nil {\n\t\t\tinfer.Logger.Printf(\"invoke: cannot locate concrete method\")\n\t\t} else {\n\t\t\tinfer.Logger.Printf(\"invoke: cannot locate concrete method: %s\", meth.String())\n\t\t}\n\t\treturn nil\n\t}\n\treturn caller.call(common, fn, common.Value, infer, b, l)\n}\n\nfunc (caller *Function) callFn(common *ssa.CallCommon, infer *TypeInfer, b *Block, l *Loop) *Function {\n\treturn caller.call(common, common.StaticCallee(), nil, infer, b, l)\n}\n\nfunc (caller *Function) call(common *ssa.CallCommon, fn *ssa.Function, rcvr ssa.Value, infer *TypeInfer, b *Block, l *Loop) *Function {\n\tcallee := caller.prepareCallFn(common, fn, rcvr)\n\tif callee.IsRecursiveCall() {\n\t\treturn callee\n\t}\n\tvisitFunc(callee.Fn, infer, callee)\n\tif callee.HasBody() {\n\t\tcallStmt := &migo.CallStatement{Name: callee.Fn.String(), Params: []*migo.Parameter{}}\n\t\tfor i, c := range common.Args {\n\t\t\tif _, ok := c.Type().(*types.Chan); ok {\n\t\t\t\tch := getChan(c, infer)\n\t\t\t\tcallStmt.AddParams(&migo.Parameter{Caller: ch, Callee: callee.Fn.Params[i]})\n\t\t\t}\n\t\t}\n\t\tif inst, ok := caller.locals[common.Value]; ok {\n\t\t\tif bindings, ok := caller.Prog.closures[inst]; ok {\n\t\t\t\tfor _, b := range bindings {\n\t\t\t\t\tif v, ok := b.(*Value); ok {\n\t\t\t\t\t\tif _, ok := derefType(v.Type()).(*types.Chan); ok {\n\t\t\t\t\t\t\tcallStmt.AddParams(&migo.Parameter{Caller: v, Callee: v})\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcaller.FuncDef.AddStmts(callStmt)\n\t}\n\treturn callee\n}\n\nfunc (caller *Function) callClosure(common *ssa.CallCommon, closure *ssa.MakeClosure, infer *TypeInfer, b *Block, l *Loop) {\n\tcallee := caller.prepareCallFn(common, closure.Fn.(*ssa.Function), nil)\n\tfor _, b := range closure.Bindings {\n\t\tif inst, ok := caller.locals[b]; ok {\n\t\t\tcallee.locals[b] = inst\n\t\t}\n\t}\n\tcallee.call(common, common.StaticCallee(), nil, infer, b, l)\n}\n\nfunc findMethod(prog *ssa.Program, meth *types.Func, typ types.Type, infer *TypeInfer) *ssa.Function {\n\tif meth != nil {\n\t\treturn prog.LookupMethod(typ, meth.Pkg(), meth.Name())\n\t}\n\tinfer.Logger.Fatal(ErrMethodNotFound)\n\treturn nil\n}\n"
  },
  {
    "path": "migoextract/chan.go",
    "content": "package migoextract\n\n// Utility functions to work with channels.\n\nimport (\n\t\"go/types\"\n\n\t\"golang.org/x/tools/go/ssa\"\n)\n\nfunc getChan(val ssa.Value, infer *TypeInfer) ssa.Value {\n\tif _, ok := val.Type().(*types.Chan); ok {\n\t\tswitch instr := val.(type) {\n\t\tcase *ssa.ChangeType:\n\t\t\treturn getChan(instr.X, infer)\n\t\tcase *ssa.Parameter:\n\t\t\treturn val // Maybe lookup from parent\n\t\tcase *ssa.MakeChan:\n\t\t\treturn val\n\t\tcase *ssa.Phi:\n\t\t\treturn val\n\t\t}\n\t}\n\tinfer.Logger.Print(\"Don't know where this chan comes from:\", val.String())\n\treturn val\n}\n"
  },
  {
    "path": "migoextract/closure.go",
    "content": "package migoextract\n\n// Captures are holders for closure capture variables.\ntype Captures []Instance\n"
  },
  {
    "path": "migoextract/commaok.go",
    "content": "package migoextract\n\n// CommaOK tests.\n\nimport (\n\t\"golang.org/x/tools/go/ssa\"\n)\n\n// CommaOK is a struct to capture different kinds of the\n// _, ok := instr syntax where instr can be TypeAssert, map Lookup or recv UnOp\ntype CommaOk struct {\n\tInstr  ssa.Instruction // TypeAssert, Lookup (map access) or UnOp (recv).\n\tResult Instance        // Result tuple { recvVal:T , recvTest:bool }.\n\tOkCond Instance        // The comma-ok condition.\n}\n\nfunc isCommaOk(f *Function, inst Instance) bool {\n\tfor _, commaOk := range f.commaok {\n\t\tif commaOk.OkCond == inst {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "migoextract/context.go",
    "content": "package migoextract\n\n// Context captures variables (and invariants) of scopes during execution.\n// Different contexts are used for different level of fine-grainedness.\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"go/types\"\n\t\"log\"\n\n\t\"github.com/nickng/migo/v3\"\n\t\"golang.org/x/tools/go/ssa\"\n)\n\n// LoopState indicate the state (in analysis) of loop currently in.\ntype LoopState int\n\n//go:generate stringer -type=LoopState\nconst (\n\tNonLoop LoopState = iota\n\tEnter             // Loop initialisation and condition checking\n\tBody              // Loop body (repeat)\n\tExit              // Loop exit\n)\n\n// LoopBound indicates if a loop is bounded or not.\ntype LoopBound int\n\n//go:generate stringer -type=LoopBound\nconst (\n\tUnknown LoopBound = iota\n\tStatic            // Static loop.\n\tDynamic           // Dynamic loop.\n)\n\n// Program captures the program environment.\n//\n// A single inference has exactly one Program, and it contains all global\n// data (and metadata) in the program.\ntype Program struct {\n\tFuncInstance map[*ssa.Function]int  // Count number of function instances.\n\tInitPkgs     map[*ssa.Package]bool  // Initialised packages.\n\tInfer        *TypeInfer             // Reference to inference.\n\tMigoProg     *migo.Program          // Core calculus of program.\n\tclosures     map[Instance]Captures  // Closures.\n\tglobals      map[ssa.Value]Instance // Global variables.\n\t*Storage                            // Storage.\n}\n\n// NewProgram creates a program for a type inference.\nfunc NewProgram(infer *TypeInfer) *Program {\n\treturn &Program{\n\t\tFuncInstance: make(map[*ssa.Function]int),\n\t\tInitPkgs:     make(map[*ssa.Package]bool),\n\t\tInfer:        infer,\n\t\tclosures:     make(map[Instance]Captures),\n\t\tglobals:      make(map[ssa.Value]Instance),\n\t\tStorage:      NewStorage(),\n\t}\n}\n\n// Function captures the function environment.\n//\n// Function environment stores local variable instances (as reference), return\n// values, if-then-else parent, select-condition.\ntype Function struct {\n\tFn          *ssa.Function           // Function callee (this).\n\tCaller      *Function               // Function caller (parent).\n\tProg        *Program                // Program environment (global).\n\tVisited     map[*ssa.BasicBlock]int // Visited block tracking.\n\tLevel       int                     // Call level (for indentation).\n\tFuncDef     *migo.Function          // Function definition.\n\tChildBlocks map[int]*Block          // Map from index -> child SSA blocks.\n\n\tid        int                    // Instance identifier.\n\thasBody   bool                   // True if function has body.\n\tcommaok   map[Instance]*CommaOk  // CommaOK statements.\n\tdefers    []*ssa.Defer           // Deferred calls.\n\tlocals    map[ssa.Value]Instance // Local variable instances.\n\trevlookup map[string]string      // Reverse lookup names.\n\textraargs []ssa.Value\n\tretvals   []Instance           // Return value instances.\n\tselects   map[Instance]*Select // Select cases mapping.\n\ttuples    map[Instance]Tuples  // Tuples.\n\tloopstack *LoopStack           // Stack of Loop.\n\t*Storage                       // Storage.\n}\n\n// NewMainFunction returns a new main() call context.\nfunc NewMainFunction(prog *Program, mainFn *ssa.Function) *Function {\n\treturn &Function{\n\t\tFn:          mainFn,\n\t\tProg:        prog,\n\t\tVisited:     make(map[*ssa.BasicBlock]int),\n\t\tFuncDef:     migo.NewFunction(\"main.main\"),\n\t\tChildBlocks: make(map[int]*Block),\n\n\t\tcommaok:   make(map[Instance]*CommaOk),\n\t\tdefers:    []*ssa.Defer{},\n\t\tlocals:    make(map[ssa.Value]Instance),\n\t\tretvals:   []Instance{},\n\t\textraargs: []ssa.Value{},\n\t\trevlookup: make(map[string]string),\n\t\tselects:   make(map[Instance]*Select),\n\t\ttuples:    make(map[Instance]Tuples),\n\t\tloopstack: NewLoopStack(),\n\t\tStorage:   NewStorage(),\n\t}\n}\n\n// NewFunction returns a new function call context, and takes the caller's\n// context as parameter.\nfunc NewFunction(caller *Function) *Function {\n\treturn &Function{\n\t\tCaller:      caller,\n\t\tProg:        caller.Prog,\n\t\tVisited:     make(map[*ssa.BasicBlock]int),\n\t\tFuncDef:     migo.NewFunction(\"__uninitialised__\"),\n\t\tLevel:       caller.Level + 1,\n\t\tChildBlocks: make(map[int]*Block),\n\n\t\tcommaok:   make(map[Instance]*CommaOk),\n\t\tdefers:    []*ssa.Defer{},\n\t\tlocals:    make(map[ssa.Value]Instance),\n\t\trevlookup: make(map[string]string),\n\t\textraargs: []ssa.Value{},\n\t\tretvals:   []Instance{},\n\t\tselects:   make(map[Instance]*Select),\n\t\ttuples:    make(map[Instance]Tuples),\n\t\tloopstack: NewLoopStack(),\n\t\tStorage:   NewStorage(),\n\t}\n}\n\n// HasBody returns true if Function is user-defined or has source code and\n// built in SSA program.\nfunc (caller *Function) HasBody() bool { return caller.hasBody }\n\n// prepareCallFn prepares a caller Function to visit performing necessary context switching and returns a new callee Function.\n// rcvr is non-nil if invoke call\nfunc (caller *Function) prepareCallFn(common *ssa.CallCommon, fn *ssa.Function, rcvr ssa.Value) *Function {\n\tcallee := NewFunction(caller)\n\tcallee.Fn = fn\n\t// This function was called before\n\tif _, ok := callee.Prog.FuncInstance[callee.Fn]; ok {\n\t\tcallee.Prog.FuncInstance[callee.Fn]++\n\t} else {\n\t\tcallee.Prog.FuncInstance[callee.Fn] = 0\n\t}\n\tcallee.FuncDef.Name = fn.String()\n\tcallee.id = callee.Prog.FuncInstance[callee.Fn]\n\tfor i, param := range callee.Fn.Params {\n\t\tvar argCaller ssa.Value\n\t\tif rcvr != nil {\n\t\t\tif i == 0 {\n\t\t\t\targCaller = rcvr\n\t\t\t} else {\n\t\t\t\targCaller = common.Args[i-1]\n\t\t\t}\n\t\t} else {\n\t\t\targCaller = common.Args[i]\n\t\t}\n\t\tif _, ok := argCaller.Type().(*types.Chan); ok {\n\t\t\tcallee.FuncDef.AddParams(&migo.Parameter{Caller: argCaller, Callee: param})\n\t\t}\n\t\tif inst, ok := caller.locals[argCaller]; ok {\n\t\t\tcallee.locals[param] = inst\n\t\t\tcallee.revlookup[argCaller.Name()] = param.Name()\n\n\t\t\t// Copy array and struct from parent.\n\t\t\tif elems, ok := caller.arrays[inst]; ok {\n\t\t\t\tcallee.arrays[inst] = elems\n\t\t\t}\n\t\t\tif fields, ok := caller.structs[inst]; ok {\n\t\t\t\tcallee.structs[inst] = fields\n\t\t\t}\n\t\t\tif maps, ok := caller.maps[inst]; ok {\n\t\t\t\tcallee.maps[inst] = maps\n\t\t\t}\n\t\t} else if c, ok := argCaller.(*ssa.Const); ok {\n\t\t\tcallee.locals[param] = &Const{c}\n\t\t}\n\t}\n\n\tif inst, ok := caller.locals[common.Value]; ok {\n\t\tif cap, ok := caller.Prog.closures[inst]; ok {\n\t\t\tfor i, fv := range callee.Fn.FreeVars {\n\t\t\t\tcallee.locals[fv] = cap[i]\n\t\t\t\tif _, ok := derefType(fv.Type()).(*types.Chan); ok {\n\t\t\t\t\tcallee.FuncDef.AddParams(&migo.Parameter{Caller: fv, Callee: fv})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn callee\n}\n\n// InstanceID returns the current function instance number (numbers of times\n// function called).\nfunc (caller *Function) InstanceID() int {\n\tif caller.id < 0 {\n\t\tlog.Fatal(ErrUnitialisedFunc)\n\t}\n\treturn caller.id\n}\n\nfunc (caller *Function) String() string {\n\tvar buf bytes.Buffer\n\tbuf.WriteString(\"--- Context ---\\n\")\n\tif caller.Fn == nil {\n\t\tlog.Fatal(ErrUnitialisedFunc)\n\t}\n\tbuf.WriteString(fmt.Sprintf(\"\\t- Fn:\\t%s_%d\\n\", caller.Fn, caller.id))\n\tif caller.Caller != nil {\n\t\tbuf.WriteString(fmt.Sprintf(\"\\t- Parent:\\t%s\\n\", caller.Caller.Fn.String()))\n\t} else {\n\t\tbuf.WriteString(\"\\t- Parent: main.main\\n\")\n\t}\n\tfor val, instance := range caller.locals {\n\t\tbuf.WriteString(fmt.Sprintf(\"\\t\\t- %s = %s\\n\", val.Name(), instance))\n\t}\n\tbuf.WriteString(fmt.Sprintf(\"\\t- Retvals: %d\\n\", len(caller.retvals)))\n\treturn buf.String()\n}\n\nfunc (caller *Function) updateInstances(old, new Instance) {\n\tfor inst, array := range caller.arrays {\n\t\tfor k, v := range array {\n\t\t\tif v == old {\n\t\t\t\tcaller.arrays[inst][k] = new\n\t\t\t}\n\t\t}\n\t}\n\tfor inst, array := range caller.Prog.arrays {\n\t\tfor k, v := range array {\n\t\t\tif v == old {\n\t\t\t\tcaller.Prog.arrays[inst][k] = new\n\t\t\t}\n\t\t}\n\t}\n\tfor inst, struc := range caller.structs {\n\t\tfor i, field := range struc {\n\t\t\tif field == old {\n\t\t\t\tcaller.structs[inst][i] = new\n\t\t\t}\n\t\t}\n\t}\n\tfor inst, struc := range caller.Prog.structs {\n\t\tfor i, field := range struc {\n\t\t\tif field == old {\n\t\t\t\tcaller.Prog.structs[inst][i] = new\n\t\t\t}\n\t\t}\n\t}\n\tfor inst, mmap := range caller.maps {\n\t\tfor k, v := range mmap {\n\t\t\tif v == old {\n\t\t\t\tcaller.maps[inst][k] = new\n\t\t\t}\n\t\t}\n\t}\n}\n\n// Block captures information about SSA block.\ntype Block struct {\n\tFunction *Function      // Parent function context.\n\tMigoDef  *migo.Function // MiGo Function for the block.\n\tPred     int            // Immediate predecessor trace.\n\tIndex    int            // Current block index.\n}\n\n// NewBlock creates a new block enclosed by the given function.\nfunc NewBlock(parent *Function, block *ssa.BasicBlock, curr int) *Block {\n\tblockFn := fmt.Sprintf(\"%s#%d\", parent.Fn.String(), block.Index)\n\tparent.ChildBlocks[block.Index] = &Block{\n\t\tFunction: parent,\n\t\tMigoDef:  migo.NewFunction(blockFn),\n\t\tPred:     curr,\n\t\tIndex:    block.Index,\n\t}\n\treturn parent.ChildBlocks[block.Index]\n}\n\n// Context is a grouping of different levels of context.\ntype Context struct {\n\tF *Function // Function context.\n\tB *Block    // Block context.\n\tL *Loop     // Loop context.\n}\n"
  },
  {
    "path": "migoextract/datastructure.go",
    "content": "package migoextract\n\n// Data structure utilities.\n\nimport (\n\t\"go/types\"\n\n\t\"golang.org/x/tools/go/ssa\"\n)\n\n// Elems are maps from array indices (variable) to VarInstances of elements.\ntype Elems map[ssa.Value]Instance\n\n// Fields is a slice of variable instances.\ntype Fields []Instance\n\nfunc (caller *Function) getStructField(struc ssa.Value, idx int) (Instance, error) {\n\tif instance, ok := caller.locals[struc]; ok {\n\t\tif fields, ok := caller.structs[instance]; ok {\n\t\t\treturn fields[idx], nil\n\t\t} else if fields, ok := caller.Prog.structs[instance]; ok {\n\t\t\treturn fields[idx], nil\n\t\t}\n\t}\n\treturn nil, ErrInvalidVarRead\n}\n\nfunc (caller *Function) setStructField(struc ssa.Value, idx int, instance Instance) {\n\tif instance, ok := caller.locals[struc]; ok {\n\t\tif _, ok := caller.structs[instance]; ok {\n\t\t\tcaller.structs[instance][idx] = instance\n\t\t\treturn\n\t\t} else if _, ok := caller.Prog.structs[instance]; ok {\n\t\t\tcaller.Prog.structs[instance][idx] = instance\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// initNestedRefVar initialises empty reference data structures {array,slice,struct} not used\n// before\nfunc initNestedRefVar(infer *TypeInfer, ctx *Context, inst Instance, heap bool) {\n\tvar s *Storage\n\tif heap {\n\t\ts = ctx.F.Prog.Storage\n\t} else {\n\t\ts = ctx.F.Storage\n\t}\n\tv, ok := inst.(*Value)\n\tif !ok {\n\t\treturn\n\t}\n\tswitch t := derefAllType(v.Type()).Underlying().(type) {\n\tcase *types.Array:\n\t\tif _, ok := s.arrays[inst]; !ok {\n\t\t\ts.arrays[inst] = make(Elems, t.Len())\n\t\t\tinfer.Logger.Print(ctx.F.Sprintf(SubSymbol+\"initialised %s as array (type: %s)\", inst, v.Type()))\n\t\t}\n\tcase *types.Slice:\n\t\tif _, ok := s.arrays[inst]; !ok {\n\t\t\ts.arrays[inst] = make(Elems, 0)\n\t\t\tinfer.Logger.Print(ctx.F.Sprintf(SubSymbol+\"initialised %s as slice (type: %s)\", inst, v.Type()))\n\t\t}\n\tcase *types.Struct:\n\t\tif _, ok := s.structs[inst]; !ok {\n\t\t\ts.structs[inst] = make(Fields, t.NumFields())\n\t\t\tinfer.Logger.Print(ctx.F.Sprintf(SubSymbol+\"initialised %s as struct (type: %s)\", inst, v.Type()))\n\t\t}\n\tdefault:\n\t}\n}\n"
  },
  {
    "path": "migoextract/error.go",
    "content": "package migoextract\n\n// Predefined errors\n\nimport \"errors\"\n\nvar (\n\tErrEmptyStack      = errors.New(\"stack: empty\")\n\tErrNoMainPkg       = errors.New(\"no main package found\")\n\tErrNonConstChanBuf = errors.New(\"MakeChan creates channel with non-const buffer size\")\n\tErrMakeChanNonChan = errors.New(\"type error: MakeChan creates non-channel type channel\")\n\tErrUnitialisedFunc = errors.New(\"operation on uninitialised function (did you call prepareVisit?)\")\n\tErrUnknownValue    = errors.New(\"internal error: unknown SSA value\")\n\tErrInvalidJumpSucc = errors.New(\"internal error: wrong number of Succ for Jump (expects 1)\")\n\tErrInvalidIfSucc   = errors.New(\"internal error: wrong number of Succ for If (expects 2)\")\n\tErrUnimplemented   = errors.New(\"unimplemented\")\n\tErrWrongArgNum     = errors.New(\"wrong number of arguments\")\n\tErrRuntimeLen      = errors.New(\"length can only be determined at runtime\")\n\tErrInvalidVarWrite = errors.New(\"internal error: write to uninitialised variable\")\n\tErrInvalidVarRead  = errors.New(\"internal error: read from uninitialised variable\")\n\tErrIfaceIncomplete = errors.New(\"interface not fully implemented\")\n\tErrMethodNotFound  = errors.New(\"interface method not found\")\n\tErrPhiUnknownEdge  = errors.New(\"phi node has edge from unknown block\")\n\tErrIncompatType    = errors.New(\"cannot convert incompatible type\")\n)\n"
  },
  {
    "path": "migoextract/instance.go",
    "content": "package migoextract\n\n// Wrapper of values and constants in an SSA program.\n// Designed for tracking usage and instances of values used in SSA program.\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"go/constant\"\n\t\"go/types\"\n\n\t\"golang.org/x/tools/go/ssa\"\n)\n\n// Instance is an interface for an instance of a defined value.\ntype Instance interface {\n\tInstance() (int, int)\n\tString() string\n}\n\n// Value captures a specific instance of a SSA value by counting the number of\n// instantiations.\ntype Value struct {\n\tssa.Value       // Storing the ssa.Value this instance is for.\n\tinstID    int   // Instance number (default: 0).\n\tloopIdx   int64 // Loop index associated with var (default: 0).\n}\n\n// Instance returns the instance identifier pair.\nfunc (i *Value) Instance() (int, int) {\n\treturn i.instID, int(i.loopIdx)\n}\n\nfunc (i *Value) String() string {\n\tvar prefix bytes.Buffer\n\tif i.Parent() != nil {\n\t\tprefix.WriteString(i.Parent().String())\n\t} else {\n\t\tprefix.WriteString(\"__main__\")\n\t}\n\treturn fmt.Sprintf(\"%s.%s_%d_%d\", prefix.String(), i.Name(), i.instID, i.loopIdx)\n}\n\n// Placeholder is a temporary stand in for actual SSA Value.\ntype Placeholder struct {\n}\n\n// Instance returns the instance number.\nfunc (i *Placeholder) Instance() (int, int) {\n\treturn -1, -1\n}\n\nfunc (i *Placeholder) String() string {\n\treturn fmt.Sprintf(\"placeholder instance\")\n}\n\n// External captures an external instance of an SSA value.\n//\n// An external instance is one without ssa.Value, usually if the creating body\n// is in runtime or not built as SSA.\ntype External struct {\n\tparent *ssa.Function // Parent (enclosing) function.\n\ttyp    types.Type    // Type of returned instance.\n\tinstID int           // Instance number (default: 0).\n}\n\n// Instance returns the instance number.\nfunc (i *External) Instance() (int, int) {\n\treturn i.instID, 0\n}\n\nfunc (i *External) String() string {\n\tvar prefix bytes.Buffer\n\tif i.parent != nil {\n\t\tprefix.WriteString(i.parent.String())\n\t} else {\n\t\tprefix.WriteString(\"__unknown__\")\n\t}\n\treturn fmt.Sprintf(\"%b.%s_%d:%s\", prefix, \"__ext\", i.instID, i.typ.String())\n}\n\n// Const captures a constant value.\n//\n// This is just a wrapper.\ntype Const struct {\n\t*ssa.Const\n}\n\n// Instance returns the instance identifier pair.\nfunc (c *Const) Instance() (int, int) { return 0, 0 }\n\nfunc (c *Const) String() string {\n\tswitch c.Const.Value.Kind() {\n\tcase constant.Bool:\n\t\treturn fmt.Sprintf(\"%s\", c.Const.String())\n\tcase constant.Complex:\n\t\treturn fmt.Sprintf(\"%v\", c.Const.Complex128())\n\tcase constant.Float:\n\t\treturn fmt.Sprintf(\"%f\", c.Const.Float64())\n\tcase constant.Int:\n\t\treturn fmt.Sprintf(\"%d\", c.Const.Int64())\n\tcase constant.String:\n\t\treturn fmt.Sprintf(\"%s\", c.Const.String())\n\tdefault:\n\t\tpanic(\"unknown constant type\")\n\t}\n}\n"
  },
  {
    "path": "migoextract/loop.go",
    "content": "package migoextract\n\n// Loop related functions and utilities.\n\nimport (\n\t\"fmt\"\n\t\"go/constant\"\n\t\"go/token\"\n\n\t\"golang.org/x/tools/go/ssa\"\n)\n\n// Loop captures information about loop.\n//\n// A Loop context exists within a function, inside the scope of a for loop.\n// Nested loops should be captured externally.\ntype Loop struct {\n\tParent *Function // Enclosing function.\n\tBound  LoopBound // Loop bound type.\n\tState  LoopState // Loop/Body/Done.\n\n\tIndexVar  ssa.Value // Variable holding the index (phi).\n\tCondVar   ssa.Value // Variable holding the cond expression.\n\tIndex     int64     // Current index value.\n\tStart     int64     // Lower bound of index.\n\tStep      int64     // Increment (can be negative).\n\tEnd       int64     // Upper bound of index.\n\tLoopBlock int       // Block number of loop (with for.loop label).\n}\n\n// SetInit sets the loop index initial value (int).\nfunc (l *Loop) SetInit(index ssa.Value, init int64) {\n\tl.IndexVar = index\n\tl.Start = init\n\tl.Index = init\n}\n\n// SetStep sets the loop index step value (int).\nfunc (l *Loop) SetStep(step int64) {\n\tl.Step = step\n}\n\n// SetCond sets the loop exit condition (int).\nfunc (l *Loop) SetCond(cond ssa.Value, max int64) {\n\tl.CondVar = cond\n\tl.End = max\n}\n\n// Next performs an index increment (e.g. i++) if possible.\nfunc (l *Loop) Next() {\n\tif l.Bound == Static {\n\t\tl.Index += l.Step\n\t}\n}\n\n// HasNext returns true if the loop should continue.\nfunc (l *Loop) HasNext() bool {\n\tif l.Bound == Static {\n\t\treturn l.Start <= l.Index && l.Index <= l.End\n\t}\n\treturn false\n}\n\nfunc (l *Loop) String() string {\n\tif l.Bound != Unknown && l.State != NonLoop {\n\t\treturn fmt.Sprintf(\"%s: bound %s [%d..%d..%d] Step:%d\", l.State, l.Bound, l.Start, l.Index, l.End, l.Step)\n\t}\n\treturn fmt.Sprintf(\"%s: bound %s\", l.State, l.Bound)\n}\n\n// loopSetIndex handles loop indices (initial value and increment)\nfunc loopSetIndex(instr *ssa.Phi, infer *TypeInfer, ctx *Context) {\n\tif i, ok := instr.Edges[0].(*ssa.Const); ok && !i.IsNil() && i.Value.Kind() == constant.Int {\n\t\tctx.L.SetInit(instr, i.Int64())\n\t\tinfer.Logger.Printf(ctx.F.Sprintf(LoopSymbol+\"%s <= i\", fmtLoopHL(ctx.L.Start)))\n\t}\n\tif bin, ok := instr.Edges[1].(*ssa.BinOp); ok {\n\t\tswitch bin.Op {\n\t\tcase token.ADD:\n\t\t\tif i, ok := bin.Y.(*ssa.Const); ok && i.Value.Kind() == constant.Int {\n\t\t\t\tctx.L.SetStep(i.Int64())\n\t\t\t}\n\t\t\tinfer.Logger.Printf(ctx.F.Sprintf(LoopSymbol+\"i += %s\", fmtLoopHL(ctx.L.Step)))\n\t\tcase token.SUB:\n\t\t\tif i, ok := bin.Y.(*ssa.Const); ok && i.Value.Kind() == constant.Int {\n\t\t\t\tctx.L.SetStep(-i.Int64())\n\t\t\t}\n\t\t\tinfer.Logger.Printf(ctx.F.Sprintf(LoopSymbol+\"i -= %s\", fmtLoopHL(ctx.L.Step)))\n\t\tdefault:\n\t\t\tinfer.Logger.Printf(\"loop index expression not supported %s\", bin)\n\t\t}\n\t}\n}\n\n// loopDetectBounds detects static bounds (or set as dynamic bounds) from based\n// on loop state machine.\nfunc loopDetectBounds(instr *ssa.Phi, infer *TypeInfer, ctx *Context) {\n\tswitch ctx.L.State {\n\tcase Enter:\n\t\tswitch ctx.L.Bound {\n\t\tcase Unknown:\n\t\t\tphiSelectEdge(instr, infer, ctx)\n\t\t\tloopSetIndex(instr, infer, ctx)\n\t\tcase Static:\n\t\t\tswitch ctx.L.Bound {\n\t\t\tcase Static:\n\t\t\t\tphiSelectEdge(instr, infer, ctx)\n\t\t\t\tif instr == ctx.L.IndexVar {\n\t\t\t\t\tctx.L.Next()\n\t\t\t\t\tinfer.Logger.Printf(ctx.F.Sprintf(LoopSymbol+\"Increment %s by %s to %s\", ctx.L.IndexVar.Name(), fmtLoopHL(ctx.L.Step), fmtLoopHL(ctx.L.Index)))\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\tvisitSkip(instr, infer, ctx)\n\t\t\t}\n\t\tcase Dynamic:\n\t\t\tphiSelectEdge(instr, infer, ctx)\n\t\t\tinfer.Logger.Printf(ctx.F.Sprintf(PhiSymbol+\"(dynamic bound) %s\", instr.String()))\n\t\t}\n\tdefault:\n\t\tphiSelectEdge(instr, infer, ctx)\n\t}\n}\n\n// loopStateTransition updates loop transitions based on the state machine.\n//\n// ... NonLoop --> Enter --> Body --> Exit ...\n//                       <--\nfunc loopStateTransition(blk *ssa.BasicBlock, infer *TypeInfer, f *Function, l **Loop) {\n\tswitch (*l).State {\n\tcase NonLoop:\n\t\tif blk.Comment == \"for.loop\" {\n\t\t\t(*l).State = Enter\n\t\t\t(*l).Bound = Unknown\n\t\t\t(*l).LoopBlock = blk.Index\n\t\t}\n\t\tif blk.Comment == \"for.body\" {\n\t\t\t(*l).State = Body\n\t\t\t(*l).Bound = Unknown\n\t\t\t(*l).LoopBlock = blk.Index\n\t\t}\n\tcase Enter:\n\t\tif blk.Comment == \"for.body\" {\n\t\t\t(*l).State = Body\n\t\t}\n\t\tif blk.Comment == \"for.done\" {\n\t\t\t(*l).State = Exit\n\t\t\ttop, err := f.loopstack.Pop()\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\t*l = top\n\t\t}\n\tcase Body:\n\t\tif blk.Comment == \"for.loop\" {\n\t\t\tif (*l).LoopBlock == blk.Index {\n\t\t\t\t// Back to loop init, but we don't need to find loop bounds\n\t\t\t\t(*l).State = Enter\n\t\t\t} else {\n\t\t\t\tif (*l).IndexVar != nil {\n\t\t\t\t\tinfer.Logger.Print(f.Sprintf(LoopSymbol+\"enter NESTED loop (%s)\", (*l).IndexVar.Name()))\n\t\t\t\t} else {\n\t\t\t\t\tinfer.Logger.Print(f.Sprintf(LoopSymbol + \"enter NESTED loop\"))\n\t\t\t\t}\n\t\t\t\tf.loopstack.Push(*l)\n\t\t\t\t*l = &Loop{Parent: f, Bound: Unknown, State: Enter, LoopBlock: blk.Index}\n\t\t\t}\n\t\t}\n\t\tif blk.Comment == \"for.done\" {\n\t\t\t(*l).State = Exit\n\t\t}\n\tcase Exit:\n\t\t(*l).State = NonLoop\n\t\t(*l).Bound = Unknown\n\t}\n}\n"
  },
  {
    "path": "migoextract/loopbound_string.go",
    "content": "// generated by stringer -type=LoopBound; DO NOT EDIT\n\npackage migoextract\n\nimport \"fmt\"\n\nconst _LoopBound_name = \"UnknownStaticDynamic\"\n\nvar _LoopBound_index = [...]uint8{0, 7, 13, 20}\n\nfunc (i LoopBound) String() string {\n\tif i < 0 || i >= LoopBound(len(_LoopBound_index)-1) {\n\t\treturn fmt.Sprintf(\"LoopBound(%d)\", i)\n\t}\n\treturn _LoopBound_name[_LoopBound_index[i]:_LoopBound_index[i+1]]\n}\n"
  },
  {
    "path": "migoextract/loopstack.go",
    "content": "package migoextract\n\nimport (\n\t\"sync\"\n)\n\n// LoopStack is a stack of ssa.BasicBlock\ntype LoopStack struct {\n\tsync.Mutex\n\ts []*Loop\n}\n\n// NewLoopStack creates a new LoopStack.\nfunc NewLoopStack() *LoopStack {\n\treturn &LoopStack{s: []*Loop{}}\n}\n\n// Push adds a new LoopContext to the top of stack.\nfunc (s *LoopStack) Push(l *Loop) {\n\ts.Lock()\n\tdefer s.Unlock()\n\ts.s = append(s.s, l)\n}\n\n// Pop removes a BasicBlock from top of stack.\nfunc (s *LoopStack) Pop() (*Loop, error) {\n\ts.Lock()\n\tdefer s.Unlock()\n\n\tsize := len(s.s)\n\tif size == 0 {\n\t\treturn nil, ErrEmptyStack\n\t}\n\tl := s.s[size-1]\n\ts.s = s.s[:size-1]\n\treturn l, nil\n}\n\n// IsEmpty returns true if stack is empty.\nfunc (s *LoopStack) IsEmpty() bool {\n\treturn len(s.s) == 0\n}\n"
  },
  {
    "path": "migoextract/loopstate_string.go",
    "content": "// generated by stringer -type=LoopState; DO NOT EDIT\n\npackage migoextract\n\nimport \"fmt\"\n\nconst _LoopState_name = \"NonLoopEnterBodyExit\"\n\nvar _LoopState_index = [...]uint8{0, 7, 12, 16, 20}\n\nfunc (i LoopState) String() string {\n\tif i < 0 || i >= LoopState(len(_LoopState_index)-1) {\n\t\treturn fmt.Sprintf(\"LoopState(%d)\", i)\n\t}\n\treturn _LoopState_name[_LoopState_index[i]:_LoopState_index[i+1]]\n}\n"
  },
  {
    "path": "migoextract/migoextract.go",
    "content": "// Package migoextract provides session type inference from Go code.\n//\npackage migoextract // import \"github.com/nickng/dingo-hunter/migoextract\"\n\nimport (\n\t\"go/types\"\n\t\"io\"\n\t\"log\"\n\t\"time\"\n\n\t\"github.com/nickng/dingo-hunter/ssabuilder\"\n\t\"github.com/nickng/migo/v3\"\n\t\"golang.org/x/tools/go/ssa\"\n)\n\n// TypeInfer contains the metadata for a type inference.\ntype TypeInfer struct {\n\tSSA    *ssabuilder.SSAInfo // SSA IR of program.\n\tEnv    *Program            // Analysed program.\n\tGQueue []*Function         // Goroutines to be analysed.\n\n\tTime   time.Duration\n\tLogger *log.Logger\n\tDone   chan struct{}\n\tError  chan error\n}\n\n// New creates a new session type infer analysis.\nfunc New(ssainfo *ssabuilder.SSAInfo, inferlog io.Writer) (*TypeInfer, error) {\n\tinfer := &TypeInfer{\n\t\tSSA:    ssainfo,\n\t\tLogger: log.New(inferlog, \"migoextract: \", ssainfo.BuildConf.LogFlags),\n\n\t\tDone:  make(chan struct{}),\n\t\tError: make(chan error, 1),\n\t}\n\n\treturn infer, nil\n}\n\n// Run executes the analysis.\nfunc (infer *TypeInfer) Run() {\n\tinfer.Logger.Println(\"---- Start Analysis ----\")\n\t// Initialise session.\n\tinfer.Env = NewProgram(infer)\n\tinfer.Env.MigoProg = migo.NewProgram()\n\n\tstartTime := time.Now()\n\tmainPkg := ssabuilder.MainPkg(infer.SSA.Prog)\n\tif mainPkg == nil {\n\t\tinfer.Error <- ErrNoMainPkg\n\t}\n\tdefer close(infer.Done)\n\n\tinitFn := mainPkg.Func(\"init\")\n\tmainFn := mainPkg.Func(\"main\")\n\n\tctx := NewMainFunction(infer.Env, mainFn)\n\t// TODO(nickng): inline initialisation of var declarations\n\tfor _, pkg := range infer.SSA.Prog.AllPackages() {\n\t\tfor _, memb := range pkg.Members {\n\t\t\tswitch value := memb.(type) {\n\t\t\tcase *ssa.Global:\n\t\t\t\tctx.Prog.globals[value] = &Value{Value: value}\n\t\t\t\tswitch t := derefAllType(value.Type()).Underlying().(type) {\n\t\t\t\tcase *types.Array:\n\t\t\t\t\tctx.Prog.arrays[ctx.Prog.globals[value]] = make(Elems, t.Len())\n\t\t\t\tcase *types.Slice:\n\t\t\t\t\tctx.Prog.arrays[ctx.Prog.globals[value]] = make(Elems, 0)\n\t\t\t\tcase *types.Struct:\n\t\t\t\t\tctx.Prog.structs[ctx.Prog.globals[value]] = make(Fields, t.NumFields())\n\t\t\t\tdefault:\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tvisitFunc(initFn, infer, ctx)\n\tvisitFunc(mainFn, infer, ctx)\n\n\tinfer.RunQueue()\n\tinfer.Time = time.Now().Sub(startTime)\n}\n\n// RunQueue executes the analysis on spawned (queued) goroutines.\nfunc (infer *TypeInfer) RunQueue() {\n\tfor _, ctx := range infer.GQueue {\n\t\tinfer.Logger.Printf(\"----- Goroutine %s -----\", ctx.Fn.String())\n\t\tvisitFunc(ctx.Fn, infer, ctx)\n\t}\n}\n"
  },
  {
    "path": "migoextract/phi.go",
    "content": "package migoextract\n\n// Deal with Phi nodes.\n\nimport (\n\t\"golang.org/x/tools/go/ssa\"\n)\n\n// phiSelectEdge selects edge based on predecessor block and returns the edge index.\nfunc phiSelectEdge(instr *ssa.Phi, infer *TypeInfer, ctx *Context) (edge int) {\n\tfor i, pred := range instr.Block().Preds {\n\t\tif pred.Index == ctx.B.Pred {\n\t\t\te, ok := ctx.F.locals[instr.Edges[i]]\n\t\t\tif !ok {\n\t\t\t\tswitch t := instr.Edges[i].(type) {\n\t\t\t\tcase *ssa.Const:\n\t\t\t\t\tctx.F.locals[instr.Edges[i]] = &Const{t}\n\t\t\t\t\te, edge = ctx.F.locals[instr.Edges[i]], pred.Index\n\t\t\t\t\tinfer.Logger.Printf(ctx.F.Sprintf(PhiSymbol+\"%s/%s = %s, selected const from block %d\", instr.Name(), e, instr.String(), edge))\n\t\t\t\tcase *ssa.Function:\n\t\t\t\t\tctx.F.locals[instr.Edges[i]] = &Value{instr.Edges[i], ctx.F.InstanceID(), ctx.L.Index}\n\t\t\t\t\te, edge = ctx.F.locals[instr.Edges[i]], pred.Index\n\t\t\t\t\tinfer.Logger.Printf(ctx.F.Sprintf(PhiSymbol+\"%s/%s = %s, selected function from block %d\", instr.Name(), e, instr.String(), edge))\n\t\t\t\tcase *ssa.Call:\n\t\t\t\t\tctx.F.locals[instr.Edges[i]] = &Value{instr.Edges[i], ctx.F.InstanceID(), ctx.L.Index}\n\t\t\t\t\te, edge = ctx.F.locals[instr.Edges[i]], pred.Index\n\t\t\t\t\tinfer.Logger.Printf(ctx.F.Sprintf(PhiSymbol+\"%s/%s = %s, selected call from block %d\", instr.Name(), e, instr.String(), edge))\n\t\t\t\tcase *ssa.UnOp:\n\t\t\t\t\tctx.F.locals[instr.Edges[i]] = &Value{instr.Edges[i], ctx.F.InstanceID(), ctx.L.Index}\n\t\t\t\t\te, edge = ctx.F.locals[instr.Edges[i]], pred.Index\n\t\t\t\t\tinfer.Logger.Printf(ctx.F.Sprintf(PhiSymbol+\"%s/%s = %s, selected UnOp from block %d\", instr.Name(), e, instr.String(), edge))\n\t\t\t\tdefault:\n\t\t\t\t\tinfer.Logger.Fatalf(\"phi: create instance Edge[%d]=%#v: %s\", i, instr.Edges[i], ErrUnknownValue)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t\tctx.F.locals[instr], edge = e, pred.Index\n\t\t\tinfer.Logger.Printf(ctx.F.Sprintf(PhiSymbol+\"%s/%s = %s, selected from block %d\", instr.Name(), e, instr.String(), edge))\n\t\t\tctx.F.revlookup[instr.Name()] = instr.Edges[i].Name()\n\t\t\tif a, ok := ctx.F.arrays[e]; ok {\n\t\t\t\tctx.F.arrays[ctx.F.locals[instr]] = a\n\t\t\t}\n\t\t\tif s, ok := ctx.F.structs[e]; ok {\n\t\t\t\tctx.F.structs[ctx.F.locals[instr]] = s\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t}\n\tinfer.Logger.Fatalf(\"phi: %d->%d: %s\", ctx.B.Pred, instr.Block().Index, ErrPhiUnknownEdge)\n\treturn\n}\n"
  },
  {
    "path": "migoextract/pointer.go",
    "content": "package migoextract\n\n// Utility functions for dealing with pointers.\n\nimport (\n\t\"go/types\"\n)\n\n// derefType dereferences a pointer type once.\nfunc derefType(t types.Type) types.Type {\n\tif p, ok := t.Underlying().(*types.Pointer); ok {\n\t\treturn p.Elem()\n\t}\n\treturn t\n}\n\n// derefAllType dereferences a pointer type until its base type.\nfunc derefAllType(t types.Type) types.Type {\n\tbaseT := t\n\tfor {\n\t\tif p, ok := baseT.Underlying().(*types.Pointer); ok {\n\t\t\tbaseT = p.Elem()\n\t\t} else {\n\t\t\treturn baseT\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "migoextract/print.go",
    "content": "package migoextract\n\n// Printing and formatting utilities.\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/fatih/color\"\n)\n\nconst (\n\tBlockSymbol     = \"┝ \"\n\tCallSymbol      = \" ↘ \"\n\tExitSymbol      = \" ↙ \"\n\tChanSymbol      = \"ν \"\n\tFuncEnterSymbol = \"┌\"\n\tFuncExitSymbol  = \"└\"\n\tRecvSymbol      = \"❓ \"\n\tSendSymbol      = \"❗ \"\n\tSkipSymbol      = \"┆ \"\n\tSpawnSymbol     = \"┿ \"\n\tJumpSymbol      = \" ⇾ \"\n\tParamSymbol     = \"├param \"\n\tMoreSymbol      = \"┊ \"\n\tReturnSymbol    = \"⏎ \"\n\tLoopSymbol      = \"↻ \"\n\tPhiSymbol       = \"φ \"\n\tIfSymbol        = \"⨁ \"\n\tSelectSymbol    = \"Sel:\"\n\tSplitSymbol     = \"分\"\n\tErrorSymbol     = \" ◹ \"\n\tFieldSymbol     = \" ↦ \"\n\tNewSymbol       = \"新\"\n\tSubSymbol       = \"    ▸ \"\n\tValSymbol       = \"├ \"\n\tAssignSymbol    = \"≔\"\n)\n\nvar (\n\tfmtBlock  = color.New(color.Italic).SprintFunc()\n\tfmtChan   = color.New(color.FgRed, color.Bold).SprintFunc()\n\tfmtClose  = color.New(color.FgGreen, color.Bold).SprintFunc()\n\tfmtLoopHL = color.New(color.FgHiRed, color.Italic).SprintFunc()\n\tfmtPos    = color.New(color.FgYellow, color.Italic).SprintFunc()\n\tfmtRecv   = color.New(color.FgHiBlue).SprintFunc()\n\tfmtSend   = color.New(color.FgCyan).SprintFunc()\n\tfmtSpawn  = color.New(color.FgMagenta, color.Bold).SprintFunc()\n\tfmtType   = color.New(color.BgBlue).SprintFunc()\n)\n\n// Sprintf in current function context.\nfunc (ctx *Function) Sprintf(format string, a ...interface{}) string {\n\treturn fmt.Sprintf(strings.Repeat(\" \", ctx.Level*2)+format, a...)\n}\n"
  },
  {
    "path": "migoextract/select.go",
    "content": "package migoextract\n\nimport (\n\t\"github.com/nickng/migo/v3\"\n\t\"golang.org/x/tools/go/ssa\"\n)\n\n// Select keeps track of select statement and its branches.\ntype Select struct {\n\tInstr    *ssa.Select           // Select SSA instruction.\n\tMigoStmt *migo.SelectStatement // Select statement in MiGo.\n\tIndex    Instance              // Index (extracted from Select instruction).\n}\n"
  },
  {
    "path": "migoextract/storage.go",
    "content": "package migoextract\n\n// Storage is a grouping of auxiliary extra storage.\ntype Storage struct {\n\tarrays  map[Instance]Elems                 // Array elements.\n\tmaps    map[Instance]map[Instance]Instance // Maps.\n\tstructs map[Instance]Fields                // Struct fields.\n}\n\n// NewStorage creates a new storage.\nfunc NewStorage() *Storage {\n\treturn &Storage{\n\t\tarrays:  make(map[Instance]Elems),\n\t\tmaps:    make(map[Instance]map[Instance]Instance),\n\t\tstructs: make(map[Instance]Fields),\n\t}\n}\n"
  },
  {
    "path": "migoextract/tuple.go",
    "content": "package migoextract\n\n// Tuple utils.\n\n// Tuples are return values for function that returns multiple.\n// Can be extracted by ssa.Extract.\ntype Tuples []Instance\n"
  },
  {
    "path": "migoextract/visit.go",
    "content": "package migoextract\n\nimport (\n\t\"fmt\"\n\t\"go/constant\"\n\t\"go/token\"\n\t\"go/types\"\n\n\t\"github.com/nickng/migo/v3\"\n\t\"golang.org/x/tools/go/ssa\"\n)\n\n// visitFunc analyses function body.\nfunc visitFunc(fn *ssa.Function, infer *TypeInfer, f *Function) {\n\tinfer.Env.MigoProg.AddFunction(f.FuncDef)\n\n\tinfer.Logger.Printf(f.Sprintf(FuncEnterSymbol+\"───── func %s ─────\", fn.Name()))\n\tdefer infer.Logger.Printf(f.Sprintf(FuncExitSymbol+\"───── func %s ─────\", fn.Name()))\n\tif fn.Name() == \"init\" {\n\t\tif _, ok := f.Prog.InitPkgs[fn.Package()]; !ok {\n\t\t\tf.Prog.InitPkgs[fn.Package()] = true\n\t\t}\n\t\tf.hasBody = true\n\t\treturn\n\t}\n\tfor val, instance := range f.locals {\n\t\tinfer.Logger.Printf(f.Sprintf(ParamSymbol+\"%s = %s\", val.Name(), instance))\n\t\tf.revlookup[instance.String()] = val.Name() // If it comes from params..\n\t}\n\n\tif fn.Blocks == nil {\n\t\tinfer.Logger.Print(f.Sprintf(MoreSymbol + \"« no function body »\"))\n\t\tf.hasBody = false // No body\n\t\treturn\n\t}\n\t// When entering function, always visit as block 0\n\tblock0 := NewBlock(f, fn.Blocks[0], 0)\n\tvisitBasicBlock(fn.Blocks[0], infer, f, block0, &Loop{Parent: f})\n\tf.hasBody = true\n}\n\nfunc visitBasicBlock(blk *ssa.BasicBlock, infer *TypeInfer, f *Function, bPrev *Block, l *Loop) {\n\tloopStateTransition(blk, infer, f, &l)\n\tif l.Bound == Static && l.HasNext() {\n\t\tinfer.Logger.Printf(f.Sprintf(BlockSymbol+\"%s %d (loop %s=%d)\", fmtBlock(\"block\"), blk.Index, l.CondVar.Name(), l.Index))\n\t\t// Loop and can continue, so don't mark as visited yet\n\t} else {\n\t\tif _, ok := f.Visited[blk]; ok {\n\t\t\tinfer.Logger.Printf(f.Sprintf(BlockSymbol+\"%s %d (visited)\", fmtBlock(\"block\"), blk.Index))\n\t\t\tf.Visited[blk]++\n\t\t\tfor i := 0; i < len(f.FuncDef.Params); i++ {\n\t\t\t\tfor k, ea := range f.extraargs {\n\t\t\t\t\tif phi, ok := ea.(*ssa.Phi); ok {\n\t\t\t\t\t\tif bPrev.Index < len(phi.Edges) {\n\t\t\t\t\t\t\tfor _, e := range phi.Edges {\n\t\t\t\t\t\t\t\tif f.FuncDef.Params[i].Caller.Name() == e.Name() {\n\t\t\t\t\t\t\t\t\tf.FuncDef.Params[i].Callee = phi\n\t\t\t\t\t\t\t\t\t// Remove from extra args\n\t\t\t\t\t\t\t\t\tif k < len(f.extraargs) {\n\t\t\t\t\t\t\t\t\t\tf.extraargs = append(f.extraargs[:k], f.extraargs[k+1:]...)\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tf.extraargs = f.extraargs[:k]\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t}\n\tinfer.Logger.Printf(f.Sprintf(BlockSymbol+\"%s %d; %s\", fmtBlock(\"block\"), blk.Index, fmtLoopHL(blk.Comment)))\n\tf.Visited[blk] = 0\n\tfor _, instr := range blk.Instrs {\n\t\tvisitInstr(instr, infer, &Context{f, bPrev, l})\n\t}\n}\n\nfunc visitInstr(instr ssa.Instruction, infer *TypeInfer, ctx *Context) {\n\tswitch instr := instr.(type) {\n\tcase *ssa.Alloc:\n\t\tvisitAlloc(instr, infer, ctx)\n\tcase *ssa.BinOp:\n\t\tvisitBinOp(instr, infer, ctx)\n\tcase *ssa.Call:\n\t\tvisitCall(instr, infer, ctx)\n\tcase *ssa.ChangeInterface:\n\t\tvisitChangeInterface(instr, infer, ctx)\n\tcase *ssa.ChangeType:\n\t\tvisitChangeType(instr, infer, ctx)\n\tcase *ssa.Convert:\n\t\tvisitConvert(instr, infer, ctx)\n\tcase *ssa.DebugRef:\n\t\t//infer.Logger.Printf(ctx.F.Sprintf(SkipSymbol+\"debug\\t\\t%s\", instr))\n\tcase *ssa.Defer:\n\t\tvisitDefer(instr, infer, ctx)\n\tcase *ssa.Extract:\n\t\tvisitExtract(instr, infer, ctx)\n\tcase *ssa.FieldAddr:\n\t\tvisitFieldAddr(instr, infer, ctx)\n\tcase *ssa.Go:\n\t\tvisitGo(instr, infer, ctx)\n\tcase *ssa.If:\n\t\tvisitIf(instr, infer, ctx)\n\tcase *ssa.Index:\n\t\tvisitIndex(instr, infer, ctx)\n\tcase *ssa.IndexAddr:\n\t\tvisitIndexAddr(instr, infer, ctx)\n\tcase *ssa.Jump:\n\t\tvisitJump(instr, infer, ctx)\n\tcase *ssa.Lookup:\n\t\tvisitLookup(instr, infer, ctx)\n\tcase *ssa.MakeChan:\n\t\tvisitMakeChan(instr, infer, ctx)\n\tcase *ssa.MakeClosure:\n\t\tvisitMakeClosure(instr, infer, ctx)\n\tcase *ssa.MakeInterface:\n\t\tvisitMakeInterface(instr, infer, ctx)\n\tcase *ssa.MakeMap:\n\t\tvisitMakeMap(instr, infer, ctx)\n\tcase *ssa.MakeSlice:\n\t\tvisitMakeSlice(instr, infer, ctx)\n\tcase *ssa.MapUpdate:\n\t\tvisitMapUpdate(instr, infer, ctx)\n\tcase *ssa.Next:\n\t\tvisitNext(instr, infer, ctx)\n\tcase *ssa.Phi:\n\t\tvisitPhi(instr, infer, ctx)\n\tcase *ssa.Return:\n\t\tvisitReturn(instr, infer, ctx)\n\tcase *ssa.RunDefers:\n\t\tvisitRunDefers(instr, infer, ctx)\n\tcase *ssa.Send:\n\t\tvisitSend(instr, infer, ctx)\n\tcase *ssa.Select:\n\t\tvisitSelect(instr, infer, ctx)\n\tcase *ssa.Slice:\n\t\tvisitSlice(instr, infer, ctx)\n\tcase *ssa.Store:\n\t\tvisitStore(instr, infer, ctx)\n\tcase *ssa.TypeAssert:\n\t\tvisitTypeAssert(instr, infer, ctx)\n\tcase *ssa.UnOp:\n\t\tswitch instr.Op {\n\t\tcase token.ARROW:\n\t\t\tvisitRecv(instr, infer, ctx)\n\t\tcase token.MUL:\n\t\t\tvisitDeref(instr, infer, ctx)\n\t\tdefault:\n\t\t\tvisitSkip(instr, infer, ctx)\n\t\t}\n\tdefault:\n\t\tvisitSkip(instr, infer, ctx)\n\t}\n}\n\nfunc visitAlloc(instr *ssa.Alloc, infer *TypeInfer, ctx *Context) {\n\tallocType := instr.Type().(*types.Pointer).Elem()\n\tswitch t := allocType.Underlying().(type) {\n\tcase *types.Array: // Static size array\n\t\tctx.F.locals[instr] = &Value{instr, ctx.F.InstanceID(), ctx.L.Index}\n\t\tif instr.Heap {\n\t\t\tctx.F.Prog.arrays[ctx.F.locals[instr]] = make(Elems, t.Len())\n\t\t\tinfer.Logger.Print(ctx.F.Sprintf(NewSymbol+\"%s = alloc (array@heap) of type %s (%d elems)\", ctx.F.locals[instr], instr.Type(), t.Len()))\n\t\t} else {\n\t\t\tctx.F.arrays[ctx.F.locals[instr]] = make(Elems, t.Len())\n\t\t\tinfer.Logger.Print(ctx.F.Sprintf(NewSymbol+\"%s = alloc (array@local) of type %s (%d elems)\", ctx.F.locals[instr], instr.Type(), t.Len()))\n\t\t}\n\tcase *types.Struct:\n\t\tctx.F.locals[instr] = &Value{instr, ctx.F.InstanceID(), ctx.L.Index}\n\t\tif instr.Heap {\n\t\t\tctx.F.Prog.structs[ctx.F.locals[instr]] = make(Fields, t.NumFields())\n\t\t\tinfer.Logger.Print(ctx.F.Sprintf(NewSymbol+\"%s = alloc (struct@heap) of type %s (%d fields)\", ctx.F.locals[instr], instr.Type(), t.NumFields()))\n\t\t} else {\n\t\t\tctx.F.structs[ctx.F.locals[instr]] = make(Fields, t.NumFields())\n\t\t\tinfer.Logger.Print(ctx.F.Sprintf(NewSymbol+\"%s = alloc (struct@local) of type %s (%d fields)\", ctx.F.locals[instr], instr.Type(), t.NumFields()))\n\t\t}\n\tcase *types.Pointer:\n\t\tswitch pt := t.Elem().Underlying().(type) {\n\t\tcase *types.Array:\n\t\t\tctx.F.locals[instr] = &Value{instr, ctx.F.InstanceID(), ctx.L.Index}\n\t\t\tif instr.Heap {\n\t\t\t\tctx.F.Prog.arrays[ctx.F.locals[instr]] = make(Elems, pt.Len())\n\t\t\t\tinfer.Logger.Print(ctx.F.Sprintf(NewSymbol+\"%s = alloc/indirect (array@heap) of type %s (%d elems)\", ctx.F.locals[instr], instr.Type(), pt.Len()))\n\t\t\t} else {\n\t\t\t\tctx.F.locals[instr] = &Value{instr, ctx.F.InstanceID(), ctx.L.Index}\n\t\t\t\tctx.F.arrays[ctx.F.locals[instr]] = make(Elems, pt.Len())\n\t\t\t\tinfer.Logger.Print(ctx.F.Sprintf(NewSymbol+\"%s = alloc/indirect (array@local) of type %s (%d elems)\", ctx.F.locals[instr], instr.Type(), pt.Len()))\n\t\t\t}\n\t\tcase *types.Struct:\n\t\t\tctx.F.locals[instr] = &Value{instr, ctx.F.InstanceID(), ctx.L.Index}\n\t\t\tif instr.Heap {\n\t\t\t\tctx.F.Prog.structs[ctx.F.locals[instr]] = make(Fields, pt.NumFields())\n\t\t\t\tinfer.Logger.Print(ctx.F.Sprintf(NewSymbol+\"%s = alloc/indirect (struct@heap) of type %s (%d fields)\", ctx.F.locals[instr], instr.Type(), pt.NumFields()))\n\t\t\t} else {\n\t\t\t\tctx.F.structs[ctx.F.locals[instr]] = make(Fields, pt.NumFields())\n\t\t\t\tinfer.Logger.Print(ctx.F.Sprintf(NewSymbol+\"%s = alloc/indirect (struct@local) of type %s (%d fields)\", ctx.F.locals[instr], instr.Type(), pt.NumFields()))\n\t\t\t}\n\t\tdefault:\n\t\t\tctx.F.locals[instr] = &Value{instr, ctx.F.InstanceID(), ctx.L.Index}\n\t\t\tinfer.Logger.Print(ctx.F.Sprintf(NewSymbol+\"%s = alloc/indirect of type %s\", ctx.F.locals[instr], instr.Type().Underlying()))\n\t\t}\n\tdefault:\n\t\tctx.F.locals[instr] = &Value{instr, ctx.F.InstanceID(), ctx.L.Index}\n\t\tinfer.Logger.Print(ctx.F.Sprintf(NewSymbol+\"%s = alloc of type %s\", ctx.F.locals[instr], instr.Type().Underlying()))\n\t}\n}\n\nfunc visitBinOp(instr *ssa.BinOp, infer *TypeInfer, ctx *Context) {\n\tif ctx.L.State == Enter {\n\t\tswitch ctx.L.Bound {\n\t\tcase Unknown:\n\t\t\tswitch instr.Op {\n\t\t\tcase token.LSS: // i < N\n\t\t\t\tif i, ok := instr.Y.(*ssa.Const); ok && i.Value.Kind() == constant.Int {\n\t\t\t\t\tctx.L.SetCond(instr, i.Int64()-1)\n\t\t\t\t\tif _, ok := instr.X.(*ssa.Phi); ok && ctx.L.Start < ctx.L.End {\n\t\t\t\t\t\tctx.L.Bound = Static\n\t\t\t\t\t\tinfer.Logger.Printf(ctx.F.Sprintf(LoopSymbol+\"i <= %s\", fmtLoopHL(ctx.L.End)))\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tctx.L.Bound = Dynamic\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\tcase token.LEQ: // i <= N\n\t\t\t\tif i, ok := instr.Y.(*ssa.Const); ok && i.Value.Kind() == constant.Int {\n\t\t\t\t\tctx.L.SetCond(instr, i.Int64())\n\t\t\t\t\tif _, ok := instr.X.(*ssa.Phi); ok && ctx.L.Start < ctx.L.End {\n\t\t\t\t\t\tctx.L.Bound = Static\n\t\t\t\t\t\tinfer.Logger.Printf(ctx.F.Sprintf(LoopSymbol+\"i <= %s\", fmtLoopHL(ctx.L.End)))\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tctx.L.Bound = Dynamic\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\tcase token.GTR: // i > N\n\t\t\t\tif i, ok := instr.Y.(*ssa.Const); ok && i.Value.Kind() == constant.Int {\n\t\t\t\t\tctx.L.SetCond(instr, i.Int64()+1)\n\t\t\t\t\tif _, ok := instr.X.(*ssa.Phi); ok && ctx.L.Start > ctx.L.End {\n\t\t\t\t\t\tctx.L.Bound = Static\n\t\t\t\t\t\tinfer.Logger.Printf(ctx.F.Sprintf(LoopSymbol+\"i > %s\", fmtLoopHL(ctx.L.End)))\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tctx.L.Bound = Dynamic\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\tcase token.GEQ: // i >= N\n\t\t\t\tif i, ok := instr.Y.(*ssa.Const); ok && i.Value.Kind() == constant.Int {\n\t\t\t\t\tctx.L.SetCond(instr, i.Int64())\n\t\t\t\t\tif _, ok := instr.X.(*ssa.Phi); ok && ctx.L.Start > ctx.L.End {\n\t\t\t\t\t\tctx.L.Bound = Static\n\t\t\t\t\t\tinfer.Logger.Printf(ctx.F.Sprintf(LoopSymbol+\"i >= %s\", fmtLoopHL(ctx.L.End)))\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tctx.L.Bound = Dynamic\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tctx.F.locals[instr] = &Value{instr, ctx.F.InstanceID(), ctx.L.Index}\n\tvisitSkip(instr, infer, ctx)\n}\n\nfunc visitCall(instr *ssa.Call, infer *TypeInfer, ctx *Context) {\n\tinfer.Logger.Printf(ctx.F.Sprintf(CallSymbol+\"%s = %s\", instr.Name(), instr.String()))\n\tctx.F.Call(instr, infer, ctx.B, ctx.L)\n}\n\nfunc visitChangeType(instr *ssa.ChangeType, infer *TypeInfer, ctx *Context) {\n\tinst, ok := ctx.F.locals[instr.X]\n\tif !ok {\n\t\tinfer.Logger.Fatalf(\"changetype: %s: %v → %v\", ErrUnknownValue, instr.X, instr)\n\t\treturn\n\t}\n\tctx.F.locals[instr] = inst\n\tif a, ok := ctx.F.arrays[ctx.F.locals[instr.X]]; ok {\n\t\tctx.F.arrays[ctx.F.locals[instr]] = a\n\t}\n\tif s, ok := ctx.F.structs[ctx.F.locals[instr.X]]; ok {\n\t\tctx.F.structs[ctx.F.locals[instr]] = s\n\t}\n\tif m, ok := ctx.F.maps[ctx.F.locals[instr.X]]; ok {\n\t\tctx.F.maps[ctx.F.locals[instr]] = m\n\t}\n\tinfer.Logger.Print(ctx.F.Sprintf(ValSymbol+\"%s = %s (type: %s ← %s)\", instr.Name(), instr.X.Name(), fmtType(instr.Type()), fmtType(instr.X.Type().Underlying())))\n\tctx.F.revlookup[instr.Name()] = instr.X.Name()\n\tif indirect, ok := ctx.F.revlookup[instr.X.Name()]; ok {\n\t\tctx.F.revlookup[instr.Name()] = indirect\n\t}\n\treturn\n}\n\nfunc visitChangeInterface(instr *ssa.ChangeInterface, infer *TypeInfer, ctx *Context) {\n\tinst, ok := ctx.F.locals[instr.X]\n\tif !ok {\n\t\tinfer.Logger.Fatalf(\"changeiface: %s: %v → %v\", ErrUnknownValue, instr.X, instr)\n\t}\n\tctx.F.locals[instr] = inst\n}\n\nfunc visitConvert(instr *ssa.Convert, infer *TypeInfer, ctx *Context) {\n\t_, ok := ctx.F.locals[instr.X]\n\tif !ok {\n\t\tif c, ok := instr.X.(*ssa.Const); ok {\n\t\t\tctx.F.locals[instr.X] = &Const{c}\n\t\t} else if _, ok := instr.X.(*ssa.Global); ok {\n\t\t\tinst, ok := ctx.F.Prog.globals[instr.X]\n\t\t\tif !ok {\n\t\t\t\tinfer.Logger.Fatalf(\"convert (global): %s: %+v\", ErrUnknownValue, instr.X)\n\t\t\t}\n\t\t\tctx.F.locals[instr.X] = inst\n\t\t\tinfer.Logger.Print(ctx.F.Sprintf(SkipSymbol+\"%s convert= %s (global)\", ctx.F.locals[instr], instr.X.Name()))\n\t\t\treturn\n\t\t} else {\n\t\t\tinfer.Logger.Fatalf(\"convert: %s: %+v\", ErrUnknownValue, instr.X)\n\t\t\treturn\n\t\t}\n\t}\n\tctx.F.locals[instr] = ctx.F.locals[instr.X]\n\tinfer.Logger.Print(ctx.F.Sprintf(SkipSymbol+\"%s convert= %s\", ctx.F.locals[instr], instr.X.Name()))\n}\n\nfunc visitDefer(instr *ssa.Defer, infer *TypeInfer, ctx *Context) {\n\tctx.F.defers = append(ctx.F.defers, instr)\n}\n\nfunc visitDeref(instr *ssa.UnOp, infer *TypeInfer, ctx *Context) {\n\tptr, val := instr.X, instr\n\t// Globactx.L.\n\tif _, ok := ptr.(*ssa.Global); ok {\n\t\tinst, ok := ctx.F.Prog.globals[ptr]\n\t\tif !ok {\n\t\t\tinfer.Logger.Fatalf(\"deref (global): %s: %+v\", ErrUnknownValue, ptr)\n\t\t\treturn\n\t\t}\n\t\tctx.F.locals[ptr], ctx.F.locals[val] = inst, inst\n\t\tinfer.Logger.Print(ctx.F.Sprintf(ValSymbol+\"%s deref= %s (global) of type %s\", inst, ptr, ptr.Type()))\n\t\t// Initialise global array/struct if needed.\n\t\tinitNestedRefVar(infer, ctx, ctx.F.locals[ptr], true)\n\t\treturn\n\t}\n\tif basic, ok := derefType(ptr.Type()).Underlying().(*types.Basic); ok && basic.Kind() == types.Byte {\n\t\tinfer.Logger.Print(ctx.F.Sprintf(SubSymbol+\"deref: %+v is a byte\", ptr))\n\t\t// Create new byte instance here, bytes do no need explicit allocation.\n\t\tctx.F.locals[ptr] = &Value{ptr, ctx.F.InstanceID(), ctx.L.Index}\n\t}\n\t// Locactx.L.\n\tinst, ok := ctx.F.locals[ptr]\n\tif !ok {\n\t\tinfer.Logger.Fatalf(\"deref: %s: %+v\", ErrUnknownValue, ptr)\n\t\treturn\n\t}\n\tctx.F.locals[ptr], ctx.F.locals[val] = inst, inst\n\tinfer.Logger.Print(ctx.F.Sprintf(ValSymbol+\"%s deref= %s of type %s\", val, ptr, ptr.Type()))\n\t// Initialise array/struct if needed.\n\tinitNestedRefVar(infer, ctx, ctx.F.locals[ptr], false)\n\treturn\n}\n\nfunc visitExtract(instr *ssa.Extract, infer *TypeInfer, ctx *Context) {\n\tif tupleInst, ok := ctx.F.locals[instr.Tuple]; ok {\n\t\tif _, ok := ctx.F.tuples[tupleInst]; !ok { // Tuple uninitialised\n\t\t\tinfer.Logger.Fatalf(\"extract: %s: Unexpected tuple: %+v\", ErrUnknownValue, instr)\n\t\t\treturn\n\t\t}\n\t\tif inst := ctx.F.tuples[tupleInst][instr.Index]; inst == nil {\n\t\t\tctx.F.tuples[tupleInst][instr.Index] = &Value{instr, ctx.F.InstanceID(), ctx.L.Index}\n\t\t}\n\t\tctx.F.locals[instr] = ctx.F.tuples[tupleInst][instr.Index]\n\t\tinitNestedRefVar(infer, ctx, ctx.F.locals[instr], false)\n\t\t// Detect select tuple.\n\t\tif _, ok := ctx.F.selects[tupleInst]; ok {\n\t\t\tswitch instr.Index {\n\t\t\tcase 0:\n\t\t\t\tctx.F.selects[tupleInst].Index = ctx.F.locals[instr]\n\t\t\t\tinfer.Logger.Print(ctx.F.Sprintf(SkipSymbol+\"%s = extract select{%d} (select-index) for %s\", ctx.F.locals[instr], instr.Index, instr))\n\t\t\t\treturn\n\t\t\tdefault:\n\t\t\t\tinfer.Logger.Print(ctx.F.Sprintf(SkipSymbol+\"%s = extract select{%d} for %s\", ctx.F.locals[instr], instr.Index, instr))\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\t// Detect commaok tuple.\n\t\tif commaOk, ok := ctx.F.commaok[tupleInst]; ok {\n\t\t\tswitch instr.Index {\n\t\t\tcase 0:\n\t\t\t\tinfer.Logger.Print(ctx.F.Sprintf(SkipSymbol+\"%s = extract commaOk{%d} for %s\", ctx.F.locals[instr], instr.Index, instr))\n\t\t\t\treturn\n\t\t\tcase 1:\n\t\t\t\tcommaOk.OkCond = ctx.F.locals[instr]\n\t\t\t\tinfer.Logger.Print(ctx.F.Sprintf(SkipSymbol+\"%s = extract commaOk{%d} (ok-test) for %s\", ctx.F.locals[instr], instr.Index, instr))\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tinfer.Logger.Print(ctx.F.Sprintf(SkipSymbol+\"%s = tuple %s[%d] of %d\", ctx.F.locals[instr], tupleInst, instr.Index, len(ctx.F.tuples[tupleInst])))\n\t\treturn\n\t}\n}\n\nfunc visitField(instr *ssa.Field, infer *TypeInfer, ctx *Context) {\n\tfield, struc, index := instr, instr.X, instr.Field\n\tif sType, ok := struc.Type().Underlying().(*types.Struct); ok {\n\t\tsInst, ok := ctx.F.locals[struc]\n\t\tif !ok {\n\t\t\tinfer.Logger.Fatalf(\"field: %s :%+v\", ErrUnknownValue, struc)\n\t\t\treturn\n\t\t}\n\t\tfields, ok := ctx.F.structs[sInst]\n\t\tif !ok {\n\t\t\tfields, ok = ctx.F.Prog.structs[sInst]\n\t\t\tif !ok {\n\t\t\t\tinfer.Logger.Fatalf(\"field: %s: struct uninitialised %+v\", ErrUnknownValue, sInst)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tinfer.Logger.Print(ctx.F.Sprintf(ValSymbol+\"%s = %s\"+FieldSymbol+\"{%d} of type %s\", instr.Name(), sInst, index, sType.String()))\n\t\tif fields[index] != nil {\n\t\t\tinfer.Logger.Print(ctx.F.Sprintf(SubSymbol+\"accessed as %s\", fields[index]))\n\t\t} else {\n\t\t\tfields[index] = &Value{field, ctx.F.InstanceID(), ctx.L.Index}\n\t\t\tinfer.Logger.Print(ctx.F.Sprintf(SubSymbol+\"field uninitialised, set to %s\", field.Name()))\n\t\t}\n\t\tinitNestedRefVar(infer, ctx, ctx.F.locals[field], false)\n\t\tctx.F.locals[field] = fields[index]\n\t\treturn\n\t}\n\tinfer.Logger.Fatalf(\"field: %s: field is not struct: %+v\", ErrInvalidVarRead, struc)\n}\n\nfunc visitFieldAddr(instr *ssa.FieldAddr, infer *TypeInfer, ctx *Context) {\n\tfield, struc, index := instr, instr.X, instr.Field\n\tif sType, ok := derefType(struc.Type()).Underlying().(*types.Struct); ok {\n\t\tsInst, ok := ctx.F.locals[struc]\n\t\tif !ok {\n\t\t\tsInst, ok = ctx.F.Prog.globals[struc]\n\t\t\tif !ok {\n\t\t\t\tinfer.Logger.Fatalf(\"field-addr: %s: %+v\", ErrUnknownValue, struc)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\t// Check status of instance.\n\t\tswitch inst := sInst.(type) {\n\t\tcase *Value: // Continue\n\t\tcase *External: // Continue\n\t\t\tinfer.Logger.Print(ctx.F.Sprintf(SubSymbol+\"field-addr: %+v is external\", sInst))\n\t\t\tctx.F.locals[field] = inst\n\t\t\treturn\n\t\tcase *Const:\n\t\t\tinfer.Logger.Print(ctx.F.Sprintf(SubSymbol+\"field-addr: %+v is a constant\", sInst))\n\t\t\tif inst.Const.IsNil() {\n\t\t\t\tctx.F.locals[field] = inst\n\t\t\t}\n\t\t\treturn\n\t\tdefault:\n\t\t\tinfer.Logger.Fatalf(\"field-addr: %s: not instance %+v\", ErrUnknownValue, sInst)\n\t\t\treturn\n\t\t}\n\t\t// Find the struct.\n\t\tfields, ok := ctx.F.structs[sInst]\n\t\tif !ok {\n\t\t\tfields, ok = ctx.F.Prog.structs[sInst]\n\t\t\tif !ok {\n\t\t\t\tinfer.Logger.Fatalf(\"field-addr: %s: struct uninitialised %+v\", ErrUnknownValue, sInst)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tinfer.Logger.Print(ctx.F.Sprintf(ValSymbol+\"%s = %s\"+FieldSymbol+\"{%d} of type %s\", instr.Name(), sInst, index, sType.String()))\n\t\tif fields[index] != nil {\n\t\t\tinfer.Logger.Print(ctx.F.Sprintf(SubSymbol+\"accessed as %s\", fields[index]))\n\t\t} else {\n\t\t\tfields[index] = &Value{field, ctx.F.InstanceID(), ctx.L.Index}\n\t\t\tinfer.Logger.Print(ctx.F.Sprintf(SubSymbol+\"field uninitialised, set to %s\", field.Name()))\n\t\t}\n\t\tinitNestedRefVar(infer, ctx, fields[index], false)\n\t\tctx.F.locals[field] = fields[index]\n\t\treturn\n\t}\n\tinfer.Logger.Fatalf(\"field-addr: %s: field is not struct: %+v\", ErrInvalidVarRead, struc)\n}\n\nfunc visitGo(instr *ssa.Go, infer *TypeInfer, ctx *Context) {\n\tinfer.Logger.Printf(ctx.F.Sprintf(SpawnSymbol+\"%s %s\", fmtSpawn(\"spawn\"), instr))\n\tctx.F.Go(instr, infer)\n}\n\nfunc visitIf(instr *ssa.If, infer *TypeInfer, ctx *Context) {\n\tif len(instr.Block().Succs) != 2 {\n\t\tinfer.Logger.Fatal(ErrInvalidIfSucc)\n\t}\n\t// Detect and unroll ctx.L.\n\tif ctx.L.State != NonLoop && ctx.L.Bound == Static && instr.Cond == ctx.L.CondVar {\n\t\tif ctx.L.HasNext() {\n\t\t\tinfer.Logger.Printf(ctx.F.Sprintf(LoopSymbol+\"loop continue %s\", ctx.L))\n\t\t\tvisitBasicBlock(instr.Block().Succs[0], infer, ctx.F, NewBlock(ctx.F, instr.Block().Succs[0], ctx.B.Index), ctx.L)\n\t\t} else {\n\t\t\tinfer.Logger.Printf(ctx.F.Sprintf(LoopSymbol+\"loop exit %s\", ctx.L))\n\t\t\tvisitBasicBlock(instr.Block().Succs[1], infer, ctx.F, NewBlock(ctx.F, instr.Block().Succs[1], ctx.B.Index), ctx.L)\n\t\t}\n\t\treturn\n\t}\n\t// Detect Select branches.\n\tif bin, ok := instr.Cond.(*ssa.BinOp); ok && bin.Op == token.EQL {\n\t\tfor _, sel := range ctx.F.selects {\n\t\t\tif bin.X == sel.Index.(*Value).Value {\n\t\t\t\tif i, ok := bin.Y.(*ssa.Const); ok && i.Value.Kind() == constant.Int {\n\t\t\t\t\t//infer.Logger.Print(fmt.Sprintf(\"[select-%d]\", i.Int64()), ctx.F.FuncDef.String())\n\t\t\t\t\tparDef := ctx.F.FuncDef\n\t\t\t\t\tparDef.PutAway() // Save select\n\t\t\t\t\tvisitBasicBlock(instr.Block().Succs[0], infer, ctx.F, NewBlock(ctx.F, instr.Block().Succs[0], ctx.B.Index), ctx.L)\n\t\t\t\t\tctx.F.FuncDef.PutAway() // Save case\n\t\t\t\t\tselCase, err := ctx.F.FuncDef.Restore()\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tinfer.Logger.Fatal(\"select-case:\", err)\n\t\t\t\t\t}\n\t\t\t\t\tsel.MigoStmt.Cases[i.Int64()] = append(sel.MigoStmt.Cases[i.Int64()], selCase...)\n\t\t\t\t\tselParent, err := parDef.Restore()\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tinfer.Logger.Fatal(\"select-parent:\", err)\n\t\t\t\t\t}\n\t\t\t\t\tparDef.AddStmts(selParent...)\n\n\t\t\t\t\t// Test if select has default branch & if this is default\n\t\t\t\t\tif !sel.Instr.Blocking && i.Int64() == int64(len(sel.Instr.States)-1) {\n\t\t\t\t\t\tinfer.Logger.Print(ctx.F.Sprintf(SelectSymbol + \"default\"))\n\t\t\t\t\t\tif instr.Block().Succs[1].Comment == \"select.done\" {\n\t\t\t\t\t\t\t// Looks like it's empty\n\t\t\t\t\t\t\tinfer.Logger.Printf(SplitSymbol+\"Empty default branch (%d ⇾ %d)\", instr.Block().Index, instr.Block().Succs[1].Index)\n\t\t\t\t\t\t\tselDefault := &migo.CallStatement{Name: fmt.Sprintf(\"%s#%d\", ctx.F.Fn.String(), instr.Block().Succs[1].Index)}\n\t\t\t\t\t\t\tfor i := 0; i < len(ctx.F.FuncDef.Params); i++ {\n\t\t\t\t\t\t\t\tfor k, ea := range ctx.F.extraargs {\n\t\t\t\t\t\t\t\t\tif phi, ok := ea.(*ssa.Phi); ok {\n\t\t\t\t\t\t\t\t\t\tif instr.Block().Succs[1].Index < len(phi.Edges) {\n\t\t\t\t\t\t\t\t\t\t\tfor _, e := range phi.Edges {\n\t\t\t\t\t\t\t\t\t\t\t\tif ctx.F.FuncDef.Params[i].Caller.Name() == e.Name() {\n\t\t\t\t\t\t\t\t\t\t\t\t\tctx.F.FuncDef.Params[i].Callee = phi\n\t\t\t\t\t\t\t\t\t\t\t\t\t// Remove from extra args\n\t\t\t\t\t\t\t\t\t\t\t\t\tif k < len(ctx.F.extraargs) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tctx.F.extraargs = append(ctx.F.extraargs[:k], ctx.F.extraargs[k+1:]...)\n\t\t\t\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tctx.F.extraargs = ctx.F.extraargs[:k]\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t// This loop copies args from current function to Successor.\n\t\t\t\t\t\t\t\tif phi, ok := ctx.F.FuncDef.Params[i].Callee.(*ssa.Phi); ok {\n\t\t\t\t\t\t\t\t\t// Resolve in current scope if phi\n\t\t\t\t\t\t\t\t\tselDefault.AddParams(&migo.Parameter{Caller: phi.Edges[instr.Block().Succs[1].Index], Callee: ctx.F.FuncDef.Params[i].Callee})\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tselDefault.AddParams(&migo.Parameter{Caller: ctx.F.FuncDef.Params[i].Callee, Callee: ctx.F.FuncDef.Params[i].Callee})\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tfor _, ea := range ctx.F.extraargs {\n\t\t\t\t\t\t\t\tif phi, ok := ea.(*ssa.Phi); ok {\n\t\t\t\t\t\t\t\t\tselDefault.AddParams(&migo.Parameter{Caller: phi.Edges[instr.Block().Succs[1].Index], Callee: phi})\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tselDefault.AddParams(&migo.Parameter{Caller: ea, Callee: ea})\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tparDef := ctx.F.FuncDef\n\t\t\t\t\t\t\tparDef.PutAway() // Save select\n\t\t\t\t\t\t\tsel.MigoStmt.Cases[len(sel.MigoStmt.Cases)-1] = append(sel.MigoStmt.Cases[len(sel.MigoStmt.Cases)-1], selDefault)\n\t\t\t\t\t\t\tselParent, err := parDef.Restore()\n\t\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\t\tinfer.Logger.Fatal(\"select-parent:\", err)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tparDef.AddStmts(selParent...)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tparDef := ctx.F.FuncDef\n\t\t\t\t\t\t\tparDef.PutAway() // Save select\n\t\t\t\t\t\t\tvisitBasicBlock(instr.Block().Succs[1], infer, ctx.F, NewBlock(ctx.F, instr.Block().Succs[1], ctx.B.Index), ctx.L)\n\t\t\t\t\t\t\tctx.F.FuncDef.PutAway() // Save case\n\t\t\t\t\t\t\tselDefault, err := ctx.F.FuncDef.Restore()\n\t\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\t\tinfer.Logger.Fatal(\"select-default:\", err)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tsel.MigoStmt.Cases[len(sel.MigoStmt.Cases)-1] = append(sel.MigoStmt.Cases[len(sel.MigoStmt.Cases)-1], selDefault...)\n\t\t\t\t\t\t\tselParent, err := parDef.Restore()\n\t\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\t\tinfer.Logger.Fatal(\"select-parent:\", err)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tparDef.AddStmts(selParent...)\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tinfer.Logger.Printf(ctx.F.Sprintf(IfSymbol+\"select-else \"+JumpSymbol+\"%d\", instr.Block().Succs[1].Index))\n\t\t\t\t\t\tvisitBasicBlock(instr.Block().Succs[1], infer, ctx.F, NewBlock(ctx.F, instr.Block().Succs[1], ctx.B.Index), ctx.L)\n\t\t\t\t\t}\n\t\t\t\t\treturn // Select if-then-else handled\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tvar cond string\n\tif inst, ok := ctx.F.locals[instr.Cond]; ok && isCommaOk(ctx.F, inst) {\n\t\tcond = fmt.Sprintf(\"comma-ok %s\", instr.Cond.Name())\n\t} else {\n\t\tcond = fmt.Sprintf(\"%s\", instr.Cond.Name())\n\t}\n\n\t// Save parent.\n\tctx.F.FuncDef.PutAway()\n\tinfer.Logger.Printf(ctx.F.Sprintf(IfSymbol+\"if %s then\"+JumpSymbol+\"%d\", cond, instr.Block().Succs[0].Index))\n\tvisitBasicBlock(instr.Block().Succs[0], infer, ctx.F, NewBlock(ctx.F, instr.Block().Succs[0], ctx.B.Index), ctx.L)\n\t// Save then.\n\tctx.F.FuncDef.PutAway()\n\tinfer.Logger.Printf(ctx.F.Sprintf(IfSymbol+\"if %s else\"+JumpSymbol+\"%d\", cond, instr.Block().Succs[1].Index))\n\tif ctx.L.State == Body && ctx.L.LoopBlock == ctx.B.Index {\n\t\t// Infinite loop.\n\t\tinfer.Logger.Printf(ctx.F.Sprintf(LoopSymbol + \" infinite loop\"))\n\t\tstmt := &migo.CallStatement{Name: fmt.Sprintf(\"%s#%d\", ctx.F.Fn.String(), ctx.B.Index)}\n\t\tfor _, p := range ctx.F.FuncDef.Params {\n\t\t\tstmt.AddParams(&migo.Parameter{Caller: p.Callee, Callee: p.Callee})\n\t\t}\n\t\tctx.F.FuncDef.AddStmts(stmt)\n\t} else {\n\t\tvisitBasicBlock(instr.Block().Succs[1], infer, ctx.F, NewBlock(ctx.F, instr.Block().Succs[1], ctx.B.Index), ctx.L)\n\t}\n\t// Save else.\n\tctx.F.FuncDef.PutAway()\n\telseStmts, err := ctx.F.FuncDef.Restore() // Else\n\tif err != nil {\n\t\tinfer.Logger.Fatal(\"restore else:\", err)\n\t}\n\tthenStmts, err := ctx.F.FuncDef.Restore() // Then\n\tif err != nil {\n\t\tinfer.Logger.Fatal(\"restore then:\", err)\n\t}\n\tparentStmts, err := ctx.F.FuncDef.Restore() // Parent\n\tif err != nil {\n\t\tinfer.Logger.Fatal(\"restore if-then-else parent:\", err)\n\t}\n\tctx.F.FuncDef.AddStmts(parentStmts...)\n\tctx.F.FuncDef.AddStmts(&migo.IfStatement{Then: thenStmts, Else: elseStmts})\n}\n\nfunc visitIndex(instr *ssa.Index, infer *TypeInfer, ctx *Context) {\n\telem, array, index := instr, instr.X, instr.Index\n\t// Array.\n\tif aType, ok := array.Type().Underlying().(*types.Array); ok {\n\t\taInst, ok := ctx.F.locals[array]\n\t\tif !ok {\n\t\t\taInst, ok = ctx.F.Prog.globals[array]\n\t\t\tif !ok {\n\t\t\t\tinfer.Logger.Fatalf(\"index: %s: array %+v\", ErrUnknownValue, array)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\telems, ok := ctx.F.arrays[aInst]\n\t\tif !ok {\n\t\t\telems, ok = ctx.F.Prog.arrays[aInst]\n\t\t\tif !ok {\n\t\t\t\tinfer.Logger.Fatalf(\"index: %s: not an array %+v\", ErrUnknownValue, aInst)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tinfer.Logger.Print(ctx.F.Sprintf(ValSymbol+\"%s = %s\"+FieldSymbol+\"[%s] of type %s\", instr.Name(), aInst, index, aType.String()))\n\t\tif elems[index] != nil {\n\t\t\tinfer.Logger.Print(ctx.F.Sprintf(SubSymbol+\"accessed as %s\", elems[index]))\n\t\t} else {\n\t\t\telems[index] = &Value{elem, ctx.F.InstanceID(), ctx.L.Index}\n\t\t\tinfer.Logger.Printf(ctx.F.Sprintf(SubSymbol+\"elem uninitialised, set to %s\", elem.Name()))\n\t\t}\n\t\tinitNestedRefVar(infer, ctx, elems[index], false)\n\t\tctx.F.locals[elem] = elems[index]\n\t\treturn\n\t}\n}\n\nfunc visitIndexAddr(instr *ssa.IndexAddr, infer *TypeInfer, ctx *Context) {\n\telem, array, index := instr, instr.X, instr.Index\n\t// Array.\n\tif aType, ok := derefType(array.Type()).Underlying().(*types.Array); ok {\n\t\taInst, ok := ctx.F.locals[array]\n\t\tif !ok {\n\t\t\taInst, ok = ctx.F.Prog.globals[array]\n\t\t\tif !ok {\n\t\t\t\tinfer.Logger.Fatalf(\"index-addr: %s: array %+v\", ErrUnknownValue, array)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\t// Check status of instance.\n\t\tswitch inst := aInst.(type) {\n\t\tcase *Value: // Continue\n\t\tcase *External: // External\n\t\t\tinfer.Logger.Print(ctx.F.Sprintf(SubSymbol+\"index-addr: array %+v is external\", aInst))\n\t\t\tctx.F.locals[elem] = inst\n\t\tcase *Const:\n\t\t\tinfer.Logger.Print(ctx.F.Sprintf(SubSymbol+\"index-addr: array %+v is a constant\", aInst))\n\t\t\tif inst.Const.IsNil() {\n\t\t\t\tctx.F.locals[elem] = inst\n\t\t\t}\n\t\t\treturn\n\t\tdefault:\n\t\t\tinfer.Logger.Fatalf(\"index-addr: %s: array is not instance %+v\", ErrUnknownValue, aInst)\n\t\t\treturn\n\t\t}\n\t\t// Find the array.\n\t\telems, ok := ctx.F.arrays[aInst]\n\t\tif !ok {\n\t\t\telems, ok = ctx.F.Prog.arrays[aInst]\n\t\t\tif !ok {\n\t\t\t\tinfer.Logger.Fatalf(\"index-addr: %s: array uninitialised %s\", ErrUnknownValue, aInst)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tinfer.Logger.Print(ctx.F.Sprintf(ValSymbol+\"%s = %s\"+FieldSymbol+\"[%s] of type %s\", instr.Name(), aInst, index, aType.String()))\n\t\tif elems[index] != nil {\n\t\t\tinfer.Logger.Print(ctx.F.Sprintf(SubSymbol+\"accessed as %s\", elems[index]))\n\t\t} else {\n\t\t\telems[index] = &Value{elem, ctx.F.InstanceID(), ctx.L.Index}\n\t\t\tinfer.Logger.Printf(ctx.F.Sprintf(SubSymbol+\"elem uninitialised, set to %s\", elem.Name()))\n\t\t}\n\t\tinitNestedRefVar(infer, ctx, elems[index], false)\n\t\tctx.F.locals[elem] = elems[index]\n\t\treturn\n\t}\n\t// Slices.\n\tif sType, ok := derefType(array.Type()).Underlying().(*types.Slice); ok {\n\t\tsInst, ok := ctx.F.locals[array]\n\t\tif !ok {\n\t\t\tsInst, ok = ctx.F.Prog.globals[array]\n\t\t\tif !ok {\n\t\t\t\tinfer.Logger.Fatalf(\"index-addr: %s: slice %+v\", ErrUnknownValue, array)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\t// Check status of instance.\n\t\tswitch inst := sInst.(type) {\n\t\tcase *Value: // Continue\n\t\t\tif basic, ok := sType.Elem().(*types.Basic); ok && basic.Kind() == types.Byte {\n\t\t\t\tctx.F.locals[elem] = inst\n\t\t\t\treturn\n\t\t\t}\n\t\tcase *External: // External\n\t\t\tinfer.Logger.Print(ctx.F.Sprintf(SubSymbol+\"index-addr: slice %+v is external\", sInst))\n\t\t\tctx.F.locals[elem] = inst\n\t\t\treturn\n\t\tcase *Const:\n\t\t\tinfer.Logger.Print(ctx.F.Sprintf(SubSymbol+\"index-addr: slice %+v is a constant\", sInst))\n\t\t\tif inst.Const.IsNil() {\n\t\t\t\tctx.F.locals[elem] = inst\n\t\t\t}\n\t\t\treturn\n\t\tdefault:\n\t\t\tinfer.Logger.Fatalf(\"index-addr: %s: slice is not instance %+v\", ErrUnknownValue, sInst)\n\t\t\treturn\n\t\t}\n\t\t// Find the slice.\n\t\telems, ok := ctx.F.arrays[sInst]\n\t\tif !ok {\n\t\t\telems, ok = ctx.F.Prog.arrays[sInst]\n\t\t\tif !ok {\n\t\t\t\tinfer.Logger.Fatalf(\"index-addr: %s: slice uninitialised %+v\", ErrUnknownValue, sInst)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tinfer.Logger.Print(ctx.F.Sprintf(ValSymbol+\"%s = %s\"+FieldSymbol+\"[%s] (slice) of type %s\", instr.Name(), sInst, index, sType.String()))\n\t\tif elems[index] != nil {\n\t\t\tinfer.Logger.Print(ctx.F.Sprintf(SubSymbol+\"accessed as %s\", elems[index]))\n\t\t} else {\n\t\t\telems[index] = &Value{elem, ctx.F.InstanceID(), ctx.L.Index}\n\t\t\tinfer.Logger.Printf(ctx.F.Sprintf(SubSymbol+\"elem uninitialised, set to %s\", elem.Name()))\n\t\t}\n\t\tinitNestedRefVar(infer, ctx, elems[index], false)\n\t\tctx.F.locals[elem] = elems[index]\n\t\treturn\n\t}\n\tinfer.Logger.Fatalf(\"index-addr: %s: not array/slice %+v\", ErrInvalidVarRead, array)\n}\n\nfunc visitJump(jump *ssa.Jump, infer *TypeInfer, ctx *Context) {\n\tif len(jump.Block().Succs) != 1 {\n\t\tinfer.Logger.Fatal(ErrInvalidJumpSucc)\n\t}\n\tcurr, next := jump.Block(), jump.Block().Succs[0]\n\tinfer.Logger.Printf(ctx.F.Sprintf(SkipSymbol+\"block %d%s%d\", curr.Index, fmtLoopHL(JumpSymbol), next.Index))\n\tswitch ctx.L.State {\n\tcase Exit:\n\t\tctx.L.State = NonLoop\n\t}\n\tif len(next.Preds) > 1 {\n\t\tinfer.Logger.Printf(ctx.F.Sprintf(SplitSymbol+\"Jump (%d ⇾ %d) %s\", curr.Index, next.Index, ctx.L.String()))\n\t\tvar stmt *migo.CallStatement\n\t\t//if ctx.L.Bound == Static && ctx.L.HasNext() {\n\t\t//stmt = &migo.CallStatement{Name: fmt.Sprintf(\"%s#%d_loop%d\", ctx.F.Fn.String(), next.Index, ctx.L.Index), Params: []*migo.Parameter{}}\n\t\t//} else {\n\t\tstmt = &migo.CallStatement{Name: fmt.Sprintf(\"%s#%d\", ctx.F.Fn.String(), next.Index)}\n\t\tfor i := 0; i < len(ctx.F.FuncDef.Params); i++ {\n\t\t\tfor k, ea := range ctx.F.extraargs {\n\t\t\t\tif phi, ok := ea.(*ssa.Phi); ok {\n\t\t\t\t\tif jump.Block().Index < len(phi.Edges) {\n\t\t\t\t\t\tfor _, e := range phi.Edges {\n\t\t\t\t\t\t\tif ctx.F.FuncDef.Params[i].Caller.Name() == e.Name() {\n\t\t\t\t\t\t\t\tctx.F.FuncDef.Params[i].Callee = phi\n\t\t\t\t\t\t\t\t// Remove from extra args\n\t\t\t\t\t\t\t\tif k < len(ctx.F.extraargs) {\n\t\t\t\t\t\t\t\t\tctx.F.extraargs = append(ctx.F.extraargs[:k], ctx.F.extraargs[k+1:]...)\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tctx.F.extraargs = ctx.F.extraargs[:k]\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// This loop copies args from current function to Successor.\n\t\t\tif phi, ok := ctx.F.FuncDef.Params[i].Callee.(*ssa.Phi); ok {\n\t\t\t\t// Resolve in current scope if phi\n\t\t\t\tstmt.AddParams(&migo.Parameter{Caller: phi.Edges[jump.Block().Index], Callee: ctx.F.FuncDef.Params[i].Callee})\n\t\t\t} else {\n\t\t\t\tstmt.AddParams(&migo.Parameter{Caller: ctx.F.FuncDef.Params[i].Callee, Callee: ctx.F.FuncDef.Params[i].Callee})\n\t\t\t}\n\t\t}\n\t\tfor _, ea := range ctx.F.extraargs {\n\t\t\tif phi, ok := ea.(*ssa.Phi); ok {\n\t\t\t\tstmt.AddParams(&migo.Parameter{Caller: phi.Edges[jump.Block().Index], Callee: phi})\n\t\t\t} else {\n\t\t\t\tstmt.AddParams(&migo.Parameter{Caller: ea, Callee: ea})\n\t\t\t}\n\t\t}\n\t\t//}\n\t\tctx.F.FuncDef.AddStmts(stmt)\n\t\tif _, visited := ctx.F.Visited[next]; !visited {\n\t\t\tnewBlock := NewBlock(ctx.F, next, ctx.B.Index)\n\t\t\toldFunc, newFunc := ctx.F.FuncDef, newBlock.MigoDef\n\t\t\tif ctx.L.Bound == Static && ctx.L.HasNext() {\n\t\t\t\tnewFunc = migo.NewFunction(fmt.Sprintf(\"%s#%d_loop%d\", ctx.F.Fn.String(), next.Index, ctx.L.Index))\n\t\t\t}\n\t\t\tfor _, p := range stmt.Params {\n\t\t\t\tnewFunc.AddParams(&migo.Parameter{Caller: p.Callee, Callee: p.Callee})\n\t\t\t}\n\t\t\tctx.F.FuncDef = newFunc\n\t\t\tinfer.Env.MigoProg.AddFunction(newFunc)\n\t\t\tvisitBasicBlock(next, infer, ctx.F, newBlock, ctx.L)\n\t\t\tctx.F.FuncDef = oldFunc\n\t\t\treturn\n\t\t}\n\t}\n\tvisitBasicBlock(next, infer, ctx.F, NewBlock(ctx.F, next, ctx.B.Index), ctx.L)\n}\n\nfunc visitLookup(instr *ssa.Lookup, infer *TypeInfer, ctx *Context) {\n\tv, ok := ctx.F.locals[instr.X]\n\tif !ok {\n\t\tif c, ok := instr.X.(*ssa.Const); ok {\n\t\t\tctx.F.locals[instr.X] = &Const{c}\n\t\t\tv = ctx.F.locals[instr.X]\n\t\t} else {\n\t\t\tinfer.Logger.Fatalf(\"lookup: %s: %+v\", ErrUnknownValue, instr.X)\n\t\t\treturn\n\t\t}\n\t}\n\t// Lookup test.\n\tidx, ok := ctx.F.locals[instr.Index]\n\tif !ok {\n\t\tif c, ok := instr.Index.(*ssa.Const); ok {\n\t\t\tidx = &Const{c}\n\t\t} else {\n\t\t\tidx = &Value{instr.Index, ctx.F.InstanceID(), ctx.L.Index}\n\t\t}\n\t\tctx.F.locals[instr.Index] = idx\n\t}\n\tctx.F.locals[instr] = &Value{instr, ctx.F.InstanceID(), ctx.L.Index}\n\tinitNestedRefVar(infer, ctx, ctx.F.locals[instr], false)\n\tif instr.CommaOk {\n\t\tctx.F.commaok[ctx.F.locals[instr]] = &CommaOk{Instr: instr, Result: ctx.F.locals[instr]}\n\t\tctx.F.tuples[ctx.F.locals[instr]] = make(Tuples, 2) // { elem, lookupOk }\n\t}\n\tinfer.Logger.Print(ctx.F.Sprintf(SkipSymbol+\"%s = lookup %s[%s]\", ctx.F.locals[instr], v, idx))\n}\n\nfunc visitMakeChan(instr *ssa.MakeChan, infer *TypeInfer, ctx *Context) {\n\tnewch := &Value{instr, ctx.F.InstanceID(), ctx.L.Index}\n\tctx.F.locals[instr] = newch\n\tchType, ok := instr.Type().(*types.Chan)\n\tif !ok {\n\t\tinfer.Logger.Fatal(ErrMakeChanNonChan)\n\t}\n\tbufSz, ok := instr.Size.(*ssa.Const)\n\tif !ok {\n\t\tinfer.Logger.Fatal(ErrNonConstChanBuf)\n\t}\n\tinfer.Logger.Printf(ctx.F.Sprintf(ChanSymbol+\"%s = %s {t:%s, buf:%d} @ %s\",\n\t\tnewch,\n\t\tfmtChan(\"chan\"),\n\t\tchType.Elem(),\n\t\tbufSz.Int64(),\n\t\tfmtPos(infer.SSA.FSet.Position(instr.Pos()).String())))\n\tctx.F.FuncDef.AddStmts(&migo.NewChanStatement{Name: instr, Chan: newch.String(), Size: bufSz.Int64()})\n\t// Make sure it is not a duplicated extraargs\n\tvar found bool\n\tfor _, ea := range ctx.F.extraargs {\n\t\tif phi, ok := ea.(*ssa.Phi); ok {\n\t\t\tif instr.Block().Index < len(phi.Edges) {\n\t\t\t\tif phi.Edges[instr.Block().Index].Name() == instr.Name() {\n\t\t\t\t\tfound = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif !found {\n\t\tctx.F.extraargs = append(ctx.F.extraargs, instr)\n\t}\n}\n\nfunc visitMakeClosure(instr *ssa.MakeClosure, infer *TypeInfer, ctx *Context) {\n\tctx.F.locals[instr] = &Value{instr, ctx.F.InstanceID(), ctx.L.Index}\n\tctx.F.Prog.closures[ctx.F.locals[instr]] = make(Captures, len(instr.Bindings))\n\tfor i, binding := range instr.Bindings {\n\t\tctx.F.Prog.closures[ctx.F.locals[instr]][i] = ctx.F.locals[binding]\n\t}\n\tinfer.Logger.Print(ctx.F.Sprintf(NewSymbol+\"%s = make closure\", ctx.F.locals[instr]))\n}\n\nfunc visitMakeInterface(instr *ssa.MakeInterface, infer *TypeInfer, ctx *Context) {\n\tiface, ok := ctx.F.locals[instr.X]\n\tif !ok {\n\t\tif c, ok := instr.X.(*ssa.Const); ok {\n\t\t\tctx.F.locals[instr.X] = &Const{c}\n\t\t} else {\n\t\t\tinfer.Logger.Fatalf(\"make-iface: %s: %s\", ErrUnknownValue, instr.X)\n\t\t\treturn\n\t\t}\n\t}\n\tctx.F.locals[instr] = iface\n\tinfer.Logger.Print(ctx.F.Sprintf(SkipSymbol+\"%s = make-iface %s\", ctx.F.locals[instr], instr.String()))\n}\n\nfunc visitMakeMap(instr *ssa.MakeMap, infer *TypeInfer, ctx *Context) {\n\tctx.F.locals[instr] = &Value{instr, ctx.F.InstanceID(), ctx.L.Index}\n\tctx.F.maps[ctx.F.locals[instr]] = make(map[Instance]Instance)\n\tinfer.Logger.Print(ctx.F.Sprintf(SkipSymbol+\"%s = make-map\", ctx.F.locals[instr]))\n}\n\nfunc visitMakeSlice(instr *ssa.MakeSlice, infer *TypeInfer, ctx *Context) {\n\tctx.F.locals[instr] = &Value{instr, ctx.F.InstanceID(), ctx.L.Index}\n\tctx.F.arrays[ctx.F.locals[instr]] = make(Elems)\n\tinfer.Logger.Print(ctx.F.Sprintf(SkipSymbol+\"%s = make-slice\", ctx.F.locals[instr]))\n}\n\nfunc visitMapUpdate(instr *ssa.MapUpdate, infer *TypeInfer, ctx *Context) {\n\tinst, ok := ctx.F.locals[instr.Map]\n\tif !ok {\n\t\tinfer.Logger.Fatalf(\"map-update: %s: %s\", ErrUnknownValue, instr.Map)\n\t\treturn\n\t}\n\tm, ok := ctx.F.maps[inst]\n\tif !ok {\n\t\tctx.F.maps[inst] = make(map[Instance]Instance) // XXX This shouldn't happen\n\t\tm = ctx.F.maps[inst]                           // The map must be defined somewhere we skipped\n\t\tinfer.Logger.Printf(\"map-update: uninitialised map: %+v %s\", instr.Map, instr.Map.String())\n\t}\n\tk, ok := ctx.F.locals[instr.Key]\n\tif !ok {\n\t\tk = &Value{instr.Key, ctx.F.InstanceID(), ctx.L.Index}\n\t\tctx.F.locals[instr.Key] = k\n\t}\n\tv, ok := ctx.F.locals[instr.Value]\n\tif !ok {\n\t\tif c, ok := instr.Value.(*ssa.Const); ok {\n\t\t\tv = &Const{c}\n\t\t} else {\n\t\t\tv = &Value{instr.Value, ctx.F.InstanceID(), ctx.L.Index}\n\t\t}\n\t\tctx.F.locals[instr.Value] = v\n\t}\n\tm[k] = v\n\tinfer.Logger.Printf(ctx.F.Sprintf(SkipSymbol+\"%s[%s] = %s\", inst, k, v))\n}\n\nfunc visitNext(instr *ssa.Next, infer *TypeInfer, ctx *Context) {\n\tctx.F.locals[instr] = &Value{instr, ctx.F.InstanceID(), ctx.L.Index}\n\tctx.F.tuples[ctx.F.locals[instr]] = make(Tuples, 3) // { ok, k, v}\n\tinfer.Logger.Print(ctx.F.Sprintf(SkipSymbol+\"%s (ok, k, v) = next\", ctx.F.locals[instr]))\n}\n\nfunc visitPhi(instr *ssa.Phi, infer *TypeInfer, ctx *Context) {\n\tloopDetectBounds(instr, infer, ctx)\n\tif _, ok := instr.Type().(*types.Chan); ok {\n\t\t// Replace existing non-edge extra args with the same name.\n\t\tfor _, e := range instr.Edges {\n\t\tEALOOP:\n\t\t\tfor i, ea := range ctx.F.extraargs {\n\t\t\t\tif e.Name() == ea.Name() {\n\t\t\t\t\tctx.F.extraargs = append(ctx.F.extraargs[:i], ctx.F.extraargs[i+1:]...)\n\t\t\t\t\tbreak EALOOP\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tctx.F.extraargs = append(ctx.F.extraargs, instr)\n\t}\n}\n\nfunc visitRecv(instr *ssa.UnOp, infer *TypeInfer, ctx *Context) {\n\tctx.F.locals[instr] = &Value{instr, ctx.F.InstanceID(), ctx.L.Index} // received value\n\tch, ok := ctx.F.locals[instr.X]\n\tif !ok { // Channel does not exist\n\t\tinfer.Logger.Fatalf(\"recv: %s: %+v\", ErrUnknownValue, instr.X)\n\t\treturn\n\t}\n\t// Receive test.\n\tif instr.CommaOk {\n\t\tctx.F.locals[instr] = &Value{instr, ctx.F.InstanceID(), ctx.L.Index}\n\t\tctx.F.commaok[ctx.F.locals[instr]] = &CommaOk{Instr: instr, Result: ctx.F.locals[instr]}\n\t\tctx.F.tuples[ctx.F.locals[instr]] = make(Tuples, 2) // { recvVal, recvOk }\n\t}\n\tpos := infer.SSA.DecodePos(ch.(*Value).Pos())\n\tinfer.Logger.Print(ctx.F.Sprintf(RecvSymbol+\"%s = %s @ %s\", ctx.F.locals[instr], ch, fmtPos(pos)))\n\tif paramName, ok := ctx.F.revlookup[ch.String()]; ok {\n\t\tctx.F.FuncDef.AddStmts(&migo.RecvStatement{Chan: paramName})\n\t} else {\n\t\tif _, ok := instr.X.(*ssa.Phi); ok { // if it's a phi, selection is made in the parameter\n\t\t\tctx.F.FuncDef.AddStmts(&migo.RecvStatement{Chan: instr.X.Name()})\n\t\t} else {\n\t\t\tctx.F.FuncDef.AddStmts(&migo.RecvStatement{Chan: ch.(*Value).Name()})\n\t\t}\n\t}\n\n\t// Initialise received value if needed.\n\tinitNestedRefVar(infer, ctx, ctx.F.locals[instr], false)\n}\n\nfunc visitReturn(ret *ssa.Return, infer *TypeInfer, ctx *Context) {\n\tswitch len(ret.Results) {\n\tcase 0:\n\t\tinfer.Logger.Printf(ctx.F.Sprintf(ReturnSymbol))\n\tcase 1:\n\t\tif c, ok := ret.Results[0].(*ssa.Const); ok {\n\t\t\tctx.F.locals[ret.Results[0]] = &Const{c}\n\t\t}\n\t\tres, ok := ctx.F.locals[ret.Results[0]]\n\t\tif !ok {\n\t\t\tinfer.Logger.Printf(\"Returning uninitialised value %s/%s\", ret.Results[0].Name(), ctx.F.locals[ret.Results[0]])\n\t\t\treturn\n\t\t}\n\t\tctx.F.retvals = append(ctx.F.retvals, ctx.F.locals[ret.Results[0]])\n\t\tinfer.Logger.Printf(ctx.F.Sprintf(ReturnSymbol+\"return[1] %s %v\", res, ctx.F.retvals))\n\tdefault:\n\t\tfor _, res := range ret.Results {\n\t\t\tctx.F.retvals = append(ctx.F.retvals, ctx.F.locals[res])\n\t\t}\n\t\tinfer.Logger.Printf(ctx.F.Sprintf(ReturnSymbol+\"return[%d] %v\", len(ret.Results), ctx.F.retvals))\n\t}\n}\n\nfunc visitRunDefers(instr *ssa.RunDefers, infer *TypeInfer, ctx *Context) {\n\tfor i := len(ctx.F.defers) - 1; i >= 0; i-- {\n\t\tcommon := ctx.F.defers[i].Common()\n\t\tif common.StaticCallee() != nil {\n\t\t\tcallee := ctx.F.prepareCallFn(common, common.StaticCallee(), nil)\n\t\t\tvisitFunc(callee.Fn, infer, callee)\n\t\t\tif callee.HasBody() {\n\t\t\t\tcallStmt := &migo.CallStatement{Name: callee.Fn.String(), Params: []*migo.Parameter{}}\n\t\t\t\tfor _, c := range common.Args {\n\t\t\t\t\tif _, ok := c.Type().(*types.Chan); ok {\n\t\t\t\t\t\tinfer.Logger.Fatalf(\"channel in defer: %s\", ErrUnimplemented)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcallee.FuncDef.AddStmts(callStmt)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc visitSelect(instr *ssa.Select, infer *TypeInfer, ctx *Context) {\n\tctx.F.locals[instr] = &Value{instr, ctx.F.InstanceID(), ctx.L.Index}\n\tctx.F.selects[ctx.F.locals[instr]] = &Select{\n\t\tInstr:    instr,\n\t\tMigoStmt: &migo.SelectStatement{Cases: [][]migo.Statement{}},\n\t}\n\tselStmt := ctx.F.selects[ctx.F.locals[instr]].MigoStmt\n\tfor _, sel := range instr.States {\n\t\tch, ok := ctx.F.locals[sel.Chan]\n\t\tif !ok {\n\t\t\tinfer.Logger.Print(\"Select found an unknown channel\", sel.Chan.String())\n\t\t}\n\t\tvar stmt migo.Statement\n\t\t//c := getChan(ch.Var(), infer)\n\t\tswitch sel.Dir {\n\t\tcase types.SendOnly:\n\t\t\tif paramName, ok := ctx.F.revlookup[ch.String()]; ok {\n\t\t\t\tstmt = &migo.SendStatement{Chan: paramName}\n\t\t\t} else {\n\t\t\t\tif _, ok := sel.Chan.(*ssa.Phi); ok { // if it's a phi, selection is made in the parameter\n\t\t\t\t\tstmt = &migo.SendStatement{Chan: sel.Chan.Name()}\n\t\t\t\t} else {\n\t\t\t\t\tstmt = &migo.SendStatement{Chan: ch.(*Value).Name()}\n\t\t\t\t}\n\t\t\t}\n\t\tcase types.RecvOnly:\n\t\t\tif paramName, ok := ctx.F.revlookup[ch.String()]; ok {\n\t\t\t\tstmt = &migo.RecvStatement{Chan: paramName}\n\t\t\t} else {\n\t\t\t\tif _, ok := ch.(*Value); ok {\n\t\t\t\t\tif _, ok := sel.Chan.(*ssa.Phi); ok { // if it's a phi, selection is made in the parameter\n\t\t\t\t\t\tstmt = &migo.RecvStatement{Chan: sel.Chan.Name()}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tstmt = &migo.RecvStatement{Chan: ch.(*Value).Name()}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Warning: receiving from external channels (e.g. cgo)\n\t\t\t\t\t// will cause problems\n\t\t\t\t\tstmt = &migo.TauStatement{}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tselStmt.Cases = append(selStmt.Cases, []migo.Statement{stmt})\n\t}\n\t// Default case exists.\n\tif !instr.Blocking {\n\t\tselStmt.Cases = append(selStmt.Cases, []migo.Statement{&migo.TauStatement{}})\n\t}\n\tctx.F.tuples[ctx.F.locals[instr]] = make(Tuples, 2+len(selStmt.Cases)) // index + recvok + cases\n\tctx.F.FuncDef.AddStmts(selStmt)\n\tinfer.Logger.Print(ctx.F.Sprintf(SelectSymbol+\" %d cases %s = %s\", 2+len(selStmt.Cases), instr.Name(), instr.String()))\n}\n\nfunc visitSend(instr *ssa.Send, infer *TypeInfer, ctx *Context) {\n\tch, ok := ctx.F.locals[instr.Chan]\n\tif !ok {\n\t\tinfer.Logger.Fatalf(\"send: %s: %+v\", ErrUnknownValue, instr.Chan)\n\t}\n\tpos := infer.SSA.DecodePos(ch.(*Value).Pos())\n\tinfer.Logger.Printf(ctx.F.Sprintf(SendSymbol+\"%s @ %s\", ch, fmtPos(pos)))\n\tif paramName, ok := ctx.F.revlookup[ch.String()]; ok {\n\t\tctx.F.FuncDef.AddStmts(&migo.SendStatement{Chan: paramName})\n\t} else {\n\t\tif _, ok := instr.Chan.(*ssa.Phi); ok {\n\t\t\tctx.F.FuncDef.AddStmts(&migo.SendStatement{Chan: instr.Chan.Name()})\n\t\t} else {\n\t\t\tctx.F.FuncDef.AddStmts(&migo.SendStatement{Chan: ch.(*Value).Name()})\n\t\t}\n\t}\n}\n\nfunc visitSkip(instr ssa.Instruction, infer *TypeInfer, ctx *Context) {\n\tif v, isVal := instr.(ssa.Value); isVal {\n\t\tinfer.Logger.Printf(ctx.F.Sprintf(SkipSymbol+\"%T\\t%s = %s\", v, v.Name(), v.String()))\n\t\treturn\n\t}\n\tinfer.Logger.Printf(ctx.F.Sprintf(SkipSymbol+\"%T\\t%s\", instr, instr))\n}\n\nfunc visitSlice(instr *ssa.Slice, infer *TypeInfer, ctx *Context) {\n\tctx.F.locals[instr] = &Value{instr, ctx.F.InstanceID(), ctx.L.Index}\n\tif _, ok := ctx.F.locals[instr.X]; !ok {\n\t\tinfer.Logger.Fatalf(\"slice: %s: %+v\", ErrUnknownValue, instr.X)\n\t\treturn\n\t}\n\tif basic, ok := instr.Type().Underlying().(*types.Basic); ok && basic.Kind() == types.String {\n\t\tinfer.Logger.Printf(ctx.F.Sprintf(SkipSymbol+\"%s = slice on string, skipping\", ctx.F.locals[instr]))\n\t\treturn\n\t}\n\tif slice, ok := instr.Type().Underlying().(*types.Slice); ok {\n\t\tif basic, ok := slice.Elem().Underlying().(*types.Basic); ok && basic.Kind() == types.Byte {\n\t\t\tinfer.Logger.Printf(ctx.F.Sprintf(SkipSymbol+\"%s = slice on byte, skipping\", ctx.F.locals[instr]))\n\t\t\treturn\n\t\t}\n\t}\n\taInst, ok := ctx.F.arrays[ctx.F.locals[instr.X]]\n\tif !ok {\n\t\taInst, ok = ctx.F.Prog.arrays[ctx.F.locals[instr.X]]\n\t\tif !ok {\n\t\t\tswitch ctx.F.locals[instr.X].(type) {\n\t\t\tcase *Value: // Continue\n\t\t\t\tinfer.Logger.Fatalf(\"slice: %s: non-slice %+v\", ErrUnknownValue, instr.X)\n\t\t\t\treturn\n\t\t\tcase *Const:\n\t\t\t\tctx.F.arrays[ctx.F.locals[instr.X]] = make(Elems)\n\t\t\t\taInst = ctx.F.arrays[ctx.F.locals[instr.X]]\n\t\t\t\tinfer.Logger.Print(ctx.F.Sprintf(\"slice: const %s %s\", instr.X.Name(), ctx.F.locals[instr.X]))\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tctx.F.Prog.arrays[ctx.F.locals[instr]] = aInst\n\t\tinfer.Logger.Print(ctx.F.Sprintf(ValSymbol+\"%s = slice %s\", ctx.F.locals[instr], ctx.F.locals[instr.X]))\n\t\treturn\n\t}\n\tctx.F.arrays[ctx.F.locals[instr]] = aInst\n\tinfer.Logger.Print(ctx.F.Sprintf(ValSymbol+\"%s = slice %s\", ctx.F.locals[instr], ctx.F.locals[instr.X]))\n}\n\nfunc visitStore(instr *ssa.Store, infer *TypeInfer, ctx *Context) {\n\tsource, dstPtr := instr.Val, instr.Addr\n\t// Globactx.L.\n\tif _, ok := dstPtr.(*ssa.Global); ok {\n\t\tdstInst, ok := ctx.F.Prog.globals[dstPtr]\n\t\tif !ok {\n\t\t\tinfer.Logger.Fatalf(\"store (global): %s: %+v\", ErrUnknownValue, dstPtr)\n\t\t}\n\t\tinst, ok := ctx.F.locals[source]\n\t\tif !ok {\n\t\t\tinst, ok = ctx.F.Prog.globals[source]\n\t\t\tif !ok {\n\t\t\t\tif c, ok := source.(*ssa.Const); ok {\n\t\t\t\t\tinst = &Const{c}\n\t\t\t\t} else {\n\t\t\t\t\tinfer.Logger.Fatalf(\"store (global): %s: %+v\", ErrUnknownValue, source)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tctx.F.Prog.globals[dstPtr] = inst\n\t\tswitch source.Type().Underlying().(type) {\n\t\tcase *types.Array:\n\t\t\tctx.F.updateInstances(dstInst, inst)\n\t\tcase *types.Slice:\n\t\t\tctx.F.updateInstances(dstInst, inst)\n\t\tcase *types.Struct:\n\t\t\tctx.F.updateInstances(dstInst, inst)\n\t\tcase *types.Map:\n\t\t\tctx.F.updateInstances(dstInst, inst)\n\t\tdefault:\n\t\t\t// Nothing to update.\n\t\t}\n\t\tinfer.Logger.Print(ctx.F.Sprintf(ValSymbol+\"%s = %s (global)\", dstPtr.Name(), ctx.F.locals[source]))\n\t\treturn\n\t}\n\tif basic, ok := derefType(dstPtr.Type()).Underlying().(*types.Basic); ok && basic.Kind() == types.Byte {\n\t\tinfer.Logger.Print(ctx.F.Sprintf(SubSymbol+\"store: %+v is a byte\", dstPtr))\n\t\tctx.F.locals[dstPtr] = &Value{dstPtr, ctx.F.InstanceID(), ctx.L.Index}\n\t}\n\t// Locactx.L.\n\tdstInst, ok := ctx.F.locals[dstPtr]\n\tif !ok {\n\t\tinfer.Logger.Fatalf(\"store: addr %s: %+v\", ErrUnknownValue, dstPtr)\n\t}\n\tinst, ok := ctx.F.locals[source]\n\tif !ok {\n\t\tif c, ok := source.(*ssa.Const); ok {\n\t\t\tinst = &Const{c}\n\t\t} else {\n\t\t\tinfer.Logger.Printf(\"store: val %s%s: %s\", source.Name(), source.Type(), ErrUnknownValue)\n\t\t}\n\t}\n\tctx.F.locals[dstPtr] = inst\n\tswitch source.Type().Underlying().(type) {\n\tcase *types.Array:\n\t\tctx.F.updateInstances(dstInst, inst)\n\tcase *types.Slice:\n\t\tctx.F.updateInstances(dstInst, inst)\n\tcase *types.Struct:\n\t\tctx.F.updateInstances(dstInst, inst)\n\tcase *types.Map:\n\t\tctx.F.updateInstances(dstInst, inst)\n\tdefault:\n\t\t// Nothing to update.\n\t}\n\tinfer.Logger.Print(ctx.F.Sprintf(ValSymbol+\"*%s store= %s/%s\", dstPtr.Name(), source.Name(), ctx.F.locals[source]))\n\treturn\n}\n\nfunc visitTypeAssert(instr *ssa.TypeAssert, infer *TypeInfer, ctx *Context) {\n\tif iface, ok := instr.AssertedType.(*types.Interface); ok {\n\t\tif meth, _ := types.MissingMethod(instr.X.Type(), iface, true); meth == nil { // No missing methods\n\t\t\tinst, ok := ctx.F.locals[instr.X]\n\t\t\tif !ok {\n\t\t\t\tinfer.Logger.Fatalf(\"typeassert: %s: iface X %+v\", ErrUnknownValue, instr.X.Name())\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif instr.CommaOk {\n\t\t\t\tctx.F.locals[instr] = &Value{instr, ctx.F.InstanceID(), ctx.L.Index}\n\t\t\t\tctx.F.commaok[ctx.F.locals[instr]] = &CommaOk{Instr: instr, Result: ctx.F.locals[instr]}\n\t\t\t\tctx.F.tuples[ctx.F.locals[instr]] = make(Tuples, 2)\n\t\t\t\tinfer.Logger.Print(ctx.F.Sprintf(SkipSymbol+\"%s = typeassert iface %s commaok\", ctx.F.locals[instr], inst))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tctx.F.locals[instr] = inst\n\t\t\tinfer.Logger.Print(ctx.F.Sprintf(SkipSymbol+\"%s = typeassert iface %s\", ctx.F.locals[instr], inst))\n\t\t\treturn\n\t\t}\n\t\tinfer.Logger.Fatalf(\"typeassert: %s: %+v\", ErrMethodNotFound, instr)\n\t\treturn\n\t}\n\tinst, ok := ctx.F.locals[instr.X]\n\tif !ok {\n\t\tinfer.Logger.Fatalf(\"typeassert: %s: assert from %+v\", ErrUnknownValue, instr.X)\n\t\treturn\n\t}\n\tif instr.CommaOk {\n\t\tctx.F.locals[instr] = &Value{instr, ctx.F.InstanceID(), ctx.L.Index}\n\t\tctx.F.commaok[ctx.F.locals[instr]] = &CommaOk{Instr: instr, Result: ctx.F.locals[instr]}\n\t\tctx.F.tuples[ctx.F.locals[instr]] = make(Tuples, 2)\n\t\tinfer.Logger.Print(ctx.F.Sprintf(SkipSymbol+\"%s = typeassert %s commaok\", ctx.F.locals[instr], inst))\n\t\treturn\n\t}\n\tctx.F.locals[instr] = inst\n\tinfer.Logger.Print(ctx.F.Sprintf(SkipSymbol+\"%s = typeassert %s\", ctx.F.locals[instr], ctx.F.locals[instr.X]))\n\treturn\n\t//infer.Logger.Fatalf(\"typeassert: %s: %+v\", ErrIncompatType, instr)\n}\n"
  },
  {
    "path": "ssabuilder/callgraph/callgraph.go",
    "content": "// Package callgraph represents function call graph.\n//\npackage callgraph // import \"github.com/nickng/dingo-hunter/ssabuilder/callgraph\"\n\nimport (\n\t\"golang.org/x/tools/go/ssa\"\n)\n\ntype Node struct {\n\tFunc     *ssa.Function\n\tChildren []*Node\n}\n\nvar (\n\tvisitedFunc  map[*ssa.Function]bool\n\tvisitedBlock map[*ssa.BasicBlock]bool\n)\n\nfunc Build(main *ssa.Function) *Node {\n\troot := &Node{\n\t\tFunc:     main,\n\t\tChildren: []*Node{},\n\t}\n\tvisitedFunc = make(map[*ssa.Function]bool)\n\tvisitedBlock = make(map[*ssa.BasicBlock]bool)\n\tvisitedFunc[root.Func] = true\n\tvisitBlock(root.Func.Blocks[0], root)\n\treturn root\n}\n\n// FuncVisitor is an interface for analysing callgraph with a 'visit' function.\ntype FuncVisitor interface {\n\tVisit(f *ssa.Function)\n}\n\n// Traverse callgraph in depth-first order.\nfunc (node *Node) Traverse(v FuncVisitor) {\n\tv.Visit(node.Func)\n\tfor _, c := range node.Children {\n\t\tc.Traverse(v)\n\t}\n}\n\nfunc visitBlock(b *ssa.BasicBlock, node *Node) {\n\tif _, ok := visitedBlock[b]; ok {\n\t\treturn\n\t}\n\tvisitedBlock[b] = true\n\tfor _, instr := range b.Instrs {\n\t\tswitch instr := instr.(type) {\n\t\tcase *ssa.Call:\n\t\t\tif f := instr.Common().StaticCallee(); f != nil {\n\t\t\t\tif _, ok := visitedFunc[f]; !ok {\n\t\t\t\t\tvisitedFunc[f] = true\n\t\t\t\t\tnode.Children = append(node.Children, &Node{\n\t\t\t\t\t\tFunc:     f,\n\t\t\t\t\t\tChildren: []*Node{},\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\tcase *ssa.Go:\n\t\t\tif f := instr.Common().StaticCallee(); f != nil {\n\t\t\t\tif _, ok := visitedFunc[f]; !ok {\n\t\t\t\t\tvisitedFunc[f] = true\n\t\t\t\t\tnode.Children = append(node.Children, &Node{\n\t\t\t\t\t\tFunc:     f,\n\t\t\t\t\t\tChildren: []*Node{},\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\tcase *ssa.If:\n\t\t\tvisitBlock(instr.Block().Succs[0], node)\n\t\t\tvisitBlock(instr.Block().Succs[1], node)\n\t\tcase *ssa.Jump: // End of a block\n\t\t\tvisitBlock(instr.Block().Succs[0], node)\n\t\tcase *ssa.Return: // End of a function\n\t\t\tfor _, child := range node.Children {\n\t\t\t\tif _, ok := visitedFunc[child.Func]; !ok {\n\t\t\t\t\tvisitedFunc[child.Func] = true\n\t\t\t\t\tvisitBlock(child.Func.Blocks[0], child)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "ssabuilder/channel.go",
    "content": "package ssabuilder\n\n// Channel helper functions.\n// Most of the functions in this file are modified from golan.org/x/tools/oracle\n\nimport (\n\t\"go/token\"\n\t\"go/types\"\n\n\t\"golang.org/x/tools/go/ssa\"\n\t\"golang.org/x/tools/go/ssa/ssautil\"\n)\n\ntype ChanOpType int\n\nconst (\n\tChanMake ChanOpType = iota\n\tChanSend\n\tChanRecv\n\tChanClose\n)\n\n// ChanOp abstracts an ssa.Send, ssa.Unop(ARROW) or a SelectState.\ntype ChanOp struct {\n\tValue ssa.Value\n\tType  ChanOpType\n\tPos   token.Pos\n}\n\n// chanOps extract all channel operations from an instruction.\nfunc chanOps(instr ssa.Instruction) []ChanOp {\n\tvar ops []ChanOp\n\tswitch instr := instr.(type) {\n\tcase *ssa.Send:\n\t\tops = append(ops, ChanOp{instr.Chan, ChanSend, instr.Pos()})\n\tcase *ssa.UnOp:\n\t\tif instr.Op == token.ARROW {\n\t\t\tops = append(ops, ChanOp{instr.X, ChanRecv, instr.Pos()})\n\t\t}\n\tcase *ssa.Select:\n\t\tfor _, st := range instr.States {\n\t\t\tswitch st.Dir {\n\t\t\tcase types.SendOnly:\n\t\t\t\tops = append(ops, ChanOp{st.Chan, ChanSend, st.Pos})\n\t\t\tcase types.RecvOnly:\n\t\t\t\tops = append(ops, ChanOp{st.Chan, ChanRecv, st.Pos})\n\t\t\t}\n\t\t}\n\tcase ssa.CallInstruction:\n\t\tcommon := instr.Common()\n\t\tif b, ok := common.Value.(*ssa.Builtin); ok && b.Name() == \"close\" {\n\t\t\tops = append(ops, ChanOp{common.Args[0], ChanClose, common.Pos()})\n\t\t}\n\t}\n\treturn ops\n}\n\n// progChanOps extract all channels from a program.\nfunc progChanOps(prog *ssa.Program) []ChanOp {\n\tvar ops []ChanOp // all sends/receives of opposite direction\n\n\t// Look at all channel operations in the whole ssa.Program.\n\tallFuncs := ssautil.AllFunctions(prog)\n\tfor fn := range allFuncs {\n\t\tfor _, b := range fn.Blocks {\n\t\t\tfor _, instr := range b.Instrs {\n\t\t\t\tfor _, op := range chanOps(instr) {\n\t\t\t\t\tops = append(ops, op)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn ops\n}\n\n// purgeChanOps removes channels that are of different type as queryOp, i.e.\n// channel we are looking for.\nfunc purgeChanOps(ops []ChanOp, ch ssa.Value) []ChanOp {\n\ti := 0\n\tfor _, op := range ops {\n\t\tif types.Identical(op.Value.Type().Underlying().(*types.Chan).Elem(), ch.Type().Underlying().(*types.Chan).Elem()) {\n\t\t\tops[i] = op\n\t\t\ti++\n\t\t}\n\t}\n\tops = ops[:i]\n\treturn ops\n}\n"
  },
  {
    "path": "ssabuilder/errors.go",
    "content": "package ssabuilder\n\nimport \"errors\"\n\nvar ErrPtaInternal = errors.New(\"internal error: pointer analysis failed\")\n"
  },
  {
    "path": "ssabuilder/pointer.go",
    "content": "package ssabuilder\n\n// Pointer analysis helper functions.\n// Most of the functions in this file are modified from golan.org/x/tools/oracle\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\n\t\"golang.org/x/tools/go/loader\"\n\t\"golang.org/x/tools/go/pointer\"\n\t\"golang.org/x/tools/go/ssa\"\n)\n\n// Create a pointer.Config whose scope is the initial packages of lprog\n// and their dependencies.\nfunc setupPTA(prog *ssa.Program, lprog *loader.Program, ptaLog io.Writer) (*pointer.Config, error) {\n\t// TODO(adonovan): the body of this function is essentially\n\t// duplicated in all go/pointer clients.  Refactor.\n\n\t// For each initial package (specified on the command line),\n\t// if it has a main function, analyze that,\n\t// otherwise analyze its tests, if any.\n\tvar testPkgs, mains []*ssa.Package\n\tfor _, info := range lprog.InitialPackages() {\n\t\tinitialPkg := prog.Package(info.Pkg)\n\n\t\t// Add package to the pointer analysis scope.\n\t\tif initialPkg.Func(\"main\") != nil {\n\t\t\tmains = append(mains, initialPkg)\n\t\t} else {\n\t\t\ttestPkgs = append(testPkgs, initialPkg)\n\t\t}\n\t}\n\tif testPkgs != nil {\n\t\tfor _, testPkg := range testPkgs {\n\t\t\tif p := prog.CreateTestMainPackage(testPkg); p != nil {\n\t\t\t\tmains = append(mains, p)\n\t\t\t}\n\t\t}\n\t}\n\tif mains == nil {\n\t\treturn nil, fmt.Errorf(\"analysis scope has no main and no tests\")\n\t}\n\treturn &pointer.Config{\n\t\tLog:        ptaLog,\n\t\tMains:      mains,\n\t\tReflection: false, // We don't consider reflection in our analysis.\n\t}, nil\n}\n"
  },
  {
    "path": "ssabuilder/print.go",
    "content": "package ssabuilder\n\nimport (\n\t\"io\"\n\t\"sort\"\n\n\t\"github.com/nickng/dingo-hunter/ssabuilder/callgraph\"\n\t\"golang.org/x/tools/go/ssa\"\n)\n\ntype Members []ssa.Member\n\nfunc (m Members) Len() int           { return len(m) }\nfunc (m Members) Less(i, j int) bool { return m[i].Pos() < m[j].Pos() }\nfunc (m Members) Swap(i, j int)      { m[i], m[j] = m[j], m[i] }\n\n// WriteTo writes SSA IR of info.Prog to w.\nfunc (info *SSAInfo) WriteTo(w io.Writer) (int64, error) {\n\tvisitedFunc := make(map[*ssa.Function]bool)\n\tvar n int64\n\tnodeQueue := []*callgraph.Node{info.CallGraph()}\n\tfor len(nodeQueue) > 0 {\n\t\thead := nodeQueue[0]\n\t\theadFunc := head.Func\n\t\tnodeQueue = nodeQueue[1:]\n\t\tif _, ok := visitedFunc[headFunc]; !ok {\n\t\t\tvisitedFunc[headFunc] = true\n\t\t\twritten, err := headFunc.WriteTo(w)\n\t\t\tif err != nil {\n\t\t\t\treturn n, err\n\t\t\t}\n\t\t\tn += written\n\t\t}\n\t\tfor _, childNode := range head.Children {\n\t\t\tnodeQueue = append(nodeQueue, childNode)\n\t\t}\n\t}\n\treturn n, nil\n}\n\n// WriteAll writes all SSA IR to w.\nfunc (info *SSAInfo) WriteAll(w io.Writer) (int64, error) {\n\tpkgFuncs := make(map[*ssa.Package]Members)\n\tvar n int64\n\tfor _, pkg := range info.Prog.AllPackages() {\n\t\tpkgFuncs[pkg] = make(Members, 0)\n\t\tfor _, memb := range pkg.Members {\n\t\t\tif f, ok := memb.(*ssa.Function); ok {\n\t\t\t\tpkgFuncs[pkg] = append(pkgFuncs[pkg], f)\n\t\t\t}\n\t\t}\n\t\tsort.Sort(pkgFuncs[pkg])\n\t}\n\tfor pkg, funcs := range pkgFuncs {\n\t\tfor _, f := range funcs {\n\t\t\twritten, err := pkg.Func(f.Name()).WriteTo(w)\n\t\t\tif err != nil {\n\t\t\t\treturn n, err\n\t\t\t}\n\t\t\tn += written\n\t\t}\n\t}\n\treturn n, nil\n}\n"
  },
  {
    "path": "ssabuilder/ssabuild.go",
    "content": "// Package ssabuilder provides a wrapper for building SSA IR from Go source code.\n//\npackage ssabuilder // import \"github.com/nickng/dingo-hunter/ssabuilder\"\n\nimport (\n\t\"fmt\"\n\t\"go/build\"\n\t\"go/token\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"log\"\n\n\t\"golang.org/x/tools/go/loader\"\n\t\"golang.org/x/tools/go/pointer\"\n\t\"golang.org/x/tools/go/ssa\"\n\t\"golang.org/x/tools/go/ssa/ssautil\"\n\n\t\"github.com/nickng/dingo-hunter/ssabuilder/callgraph\"\n)\n\n// A Mode value is a flag indicating how the source code are supplied.\ntype Mode uint\n\nconst (\n\t// FromFiles is option to use a list of filenames for initial packages.\n\tFromFiles Mode = 1 << iota\n\n\t// FromString is option to use a string as body of initial package.\n\tFromString\n)\n\n// Config holds the configuration for building SSA IR.\ntype Config struct {\n\tBuildMode Mode\n\tFiles     []string          // (Initial) files to load.\n\tSource    string            // Source code.\n\tBuildLog  io.Writer         // Build log.\n\tPtaLog    io.Writer         // Pointer analysis log.\n\tLogFlags  int               // Flags for build/pta log.\n\tBadPkgs   map[string]string // Packages not to load (with reasons).\n}\n\n// SSAInfo is the SSA IR + metainfo built from a given Config.\ntype SSAInfo struct {\n\tBuildConf   *Config  // Build configuration (initial files, logs).\n\tIgnoredPkgs []string // Packages not loaded (respects BuildConf.BadPkgs).\n\n\tFSet    *token.FileSet  // FileSet for parsed source files.\n\tProg    *ssa.Program    // SSA IR for whole program.\n\tPtaConf *pointer.Config // Pointer analysis config.\n\n\tLogger *log.Logger // Build logger.\n}\n\nvar (\n\t// Packages that should not be loaded (and reasons) by default\n\tbadPkgs = map[string]string{\n\t\t\"fmt\":     \"Recursive calls unrelated to communication\",\n\t\t\"reflect\": \"Reflection not supported for static analyser\",\n\t\t\"runtime\": \"Runtime contains threads that are not user related\",\n\t\t\"strings\": \"Strings function does not have communication\",\n\t\t\"sync\":    \"Atomics confuse analyser\",\n\t\t\"time\":    \"Time not supported\",\n\t\t\"rand\":    \"Math does not use channels\",\n\t}\n)\n\n// NewConfig creates a new default build configuration.\nfunc NewConfig(files []string) (*Config, error) {\n\tif len(files) == 0 {\n\t\treturn nil, fmt.Errorf(\"no files specified or analysis\")\n\t}\n\treturn &Config{\n\t\tBuildMode: FromFiles,\n\t\tFiles:     files,\n\t\tBuildLog:  ioutil.Discard,\n\t\tPtaLog:    ioutil.Discard,\n\t\tLogFlags:  log.LstdFlags,\n\t\tBadPkgs:   badPkgs,\n\t}, nil\n}\n\n// NewConfigFromString creates a new default build configuration.\nfunc NewConfigFromString(s string) (*Config, error) {\n\treturn &Config{\n\t\tBuildMode: FromString,\n\t\tSource:    s,\n\t\tBuildLog:  ioutil.Discard,\n\t\tPtaLog:    ioutil.Discard,\n\t\tLogFlags:  log.LstdFlags,\n\t\tBadPkgs:   badPkgs,\n\t}, nil\n}\n\n// Build constructs the SSA IR using given config, and sets up pointer analysis.\nfunc (conf *Config) Build() (*SSAInfo, error) {\n\tvar lconf = loader.Config{Build: &build.Default}\n\tbuildLog := log.New(conf.BuildLog, \"ssabuild: \", conf.LogFlags)\n\n\tif conf.BuildMode == FromFiles {\n\t\targs, err := lconf.FromArgs(conf.Files, false /* No tests */)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif len(args) > 0 {\n\t\t\treturn nil, fmt.Errorf(\"surplus arguments: %q\", args)\n\t\t}\n\t} else if conf.BuildMode == FromString {\n\t\tf, err := lconf.ParseFile(\"\", conf.Source)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlconf.CreateFromFiles(\"\", f)\n\t} else {\n\t\tbuildLog.Fatal(\"Unknown build mode\")\n\n\t}\n\n\t// Load, parse and type-check program\n\tlprog, err := lconf.Load()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tbuildLog.Print(\"Program loaded and type checked\")\n\n\tprog := ssautil.CreateProgram(lprog, ssa.GlobalDebug|ssa.BareInits)\n\n\t// Prepare Config for whole-program pointer analysis.\n\tptaConf, err := setupPTA(prog, lprog, conf.PtaLog)\n\n\tignoredPkgs := []string{}\n\tif len(conf.BadPkgs) == 0 {\n\t\tprog.Build()\n\t} else {\n\t\tfor _, info := range lprog.AllPackages {\n\t\t\tif reason, badPkg := conf.BadPkgs[info.Pkg.Name()]; badPkg {\n\t\t\t\tbuildLog.Printf(\"Skip package: %s (%s)\", info.Pkg.Name(), reason)\n\t\t\t\tignoredPkgs = append(ignoredPkgs, info.Pkg.Name())\n\t\t\t} else {\n\t\t\t\tprog.Package(info.Pkg).Build()\n\t\t\t}\n\t\t}\n\t}\n\n\treturn &SSAInfo{\n\t\tBuildConf:   conf,\n\t\tIgnoredPkgs: ignoredPkgs,\n\t\tFSet:        lprog.Fset,\n\t\tProg:        prog,\n\t\tPtaConf:     ptaConf,\n\t\tLogger:      buildLog,\n\t}, nil\n}\n\n// CallGraph builds the call graph from the 'main.main' function.\n//\n// The call graph is rooted at 'main.main', all nodes appear only once in the\n// graph. A side-effect of building the call graph is obtaining a list of\n// functions used in a program (as functions not called will not appear in the\n// CallGraph).\n// TODO(nickng) cache previously built CallGraph.\nfunc (info *SSAInfo) CallGraph() *callgraph.Node {\n\tmainPkg := MainPkg(info.Prog)\n\tif mainFunc := mainPkg.Func(\"main\"); mainFunc != nil {\n\t\treturn callgraph.Build(mainFunc)\n\t}\n\treturn nil // No main pkg --> nothing is called\n}\n\n// DecodePos converts a token.Pos (offset) to an actual token.Position.\n//\n// This is just a shortcut to .FSet.Position.\nfunc (info *SSAInfo) DecodePos(pos token.Pos) token.Position {\n\treturn info.FSet.Position(pos)\n}\n\n// NewPta performs a custom pointer analysis on given values.\nfunc (info *SSAInfo) NewPta(vals ...ssa.Value) *pointer.Result {\n\tfor _, val := range vals {\n\t\tinfo.PtaConf.AddQuery(val)\n\t}\n\tresult, err := pointer.Analyze(info.PtaConf)\n\tif err != nil {\n\t\tinfo.Logger.Print(\"NewPta:\", ErrPtaInternal)\n\t}\n\treturn result\n}\n\n// FindChan performs a ptr analysis on a given chan ssa.Value, returns a list of\n// related ChanOp on the chan.\nfunc (info *SSAInfo) FindChan(ch ssa.Value) []ChanOp {\n\tchanOps := purgeChanOps(progChanOps(info.Prog), ch)\n\tfor _, op := range chanOps {\n\t\tinfo.PtaConf.AddQuery(op.Value)\n\t}\n\tresult, err := pointer.Analyze(info.PtaConf)\n\tif err != nil {\n\t\tinfo.Logger.Print(\"FindChan failed:\", ErrPtaInternal)\n\t}\n\tqueryCh := result.Queries[ch]\n\tvar ops []ChanOp\n\tfor _, label := range queryCh.PointsTo().Labels() {\n\t\t// Add MakeChan to result\n\t\tops = append(ops, ChanOp{label.Value(), ChanMake, label.Pos()})\n\t}\n\tfor _, op := range chanOps {\n\t\tif ptr, ok := result.Queries[op.Value]; ok && ptr.MayAlias(queryCh) {\n\t\t\tops = append(ops, op)\n\t\t}\n\t}\n\treturn ops\n}\n"
  },
  {
    "path": "ssabuilder/util.go",
    "content": "package ssabuilder\n\nimport (\n\t\"golang.org/x/tools/go/ssa\"\n)\n\n// GetMainPkg returns main package of a command.\nfunc MainPkg(prog *ssa.Program) *ssa.Package {\n\tpkgs := prog.AllPackages()\n\tfor _, pkg := range pkgs {\n\t\tif pkg.Pkg.Name() == \"main\" {\n\t\t\treturn pkg\n\t\t}\n\t}\n\treturn nil // Not found\n}\n"
  },
  {
    "path": "static/script.js",
    "content": "// goCode returns string of Go code extracted from #go div.\nfunction goCode() {\n  var code='';\n  $.each($('#go pre'), function(i, val) {\n    code += val.innerText + '\\n';\n  });\n  return code;\n}\nfunction migoCode() {\n  var code='';\n  $.each($('#out pre'), function(i, val) {\n    code += val.innerText + '\\n';\n  });\n  return code;\n}\n// writeCode puts s into the #out div.\nfunction writeTo(s, selector) {\n  $(selector).empty();\n  var strs = s.split('\\n');\n  for (var i=0; i<strs.length; i++) {\n    $(selector).append($('<pre/>').html(strs[i]+'\\n'));\n  }\n}\n// reportTime puts t to the time div.\nfunction reportTime(t) {\n  if (t!=undefined && t!=null && t!='') {\n    $('#time').html('Last operation completed in '+t);\n  } else {\n    $('#time').html('');\n  }\n}\n(function(){\n$('#ssa').on('click', function() {\n  reportTime('');\n  $.ajax({\n    url: '/ssa',\n    type: 'POST',\n    data: goCode(),\n    async: true,\n    success: function(msg) {\n      writeTo(msg, '#out');\n      $('#out').attr('lang', 'Go SSA')\n    }\n  });\n});\n$('#cfsm').on('click', function() {\n  reportTime('');\n  $.ajax({\n    url: '/cfsm',\n    type: 'POST',\n    data: goCode(),\n    async: true,\n    success: function(msg) {\n      var obj=JSON.parse(msg);\n      if (obj!=null && obj.CFSM!=null) {\n        writeTo(obj.CFSM, '#out');\n        reportTime(obj.time);\n        $('#out').attr('lang', 'CFSM');\n      } else {\n        writeTo(\"JSON error\", '#out');\n      }\n    }\n  });\n});\n$('#migo').on('click', function() {\n  reportTime('');\n  $.ajax({\n    url: '/migo',\n    type: 'POST',\n    data: goCode(),\n    async: true,\n    success: function(msg) {\n      var obj=JSON.parse(msg);\n      if (obj!=null && obj.MiGo!=null) {\n        writeTo(obj.MiGo, '#out');\n        reportTime(obj.time);\n        $('#out').attr('lang', 'MiGo');\n      } else {\n        writeTo(\"JSON error\", '#out');\n      }\n    }\n  });\n});\n$('#example').on('click', function() {\n  reportTime('');\n  $.ajax({\n    url: '/load',\n    type: 'POST',\n    data: $('#examples option:selected').text(),\n    async: true,\n    success: function(msg) {\n      writeTo(msg, '#go');\n      writeTo('No output.', '#out');\n      $('#out').removeAttr('lang');\n    }\n  });\n});\n$('#gong').on('click', function() {\n  if ($('#out').attr('lang') != 'MiGo') {\n    return false\n  }\n  reportTime('');\n  $.ajax({\n    url: '/gong',\n    type: 'POST',\n    data: migoCode(),\n    async: true,\n    success: function(msg) {\n      var obj = JSON.parse(msg);\n      if (obj!=null&&obj.Gong!=null) {\n        writeTo(obj.Gong, '#gong-output');\n        reportTime(obj.time);\n        $('#gong-wrap').addClass('visible');\n      } else {\n        writeTo(\"JSON error\", '#gong-output');\n      }\n    }\n  });\n});\n$('#gong-output-close').on('click', function() {\n  $('#gong-wrap').removeClass('visible');\n})\n$('#synthesis').on('click', function() {\n  if ($('#out').attr('lang') != 'CFSM') {\n    return false\n  }\n  reportTime('');\n  $.ajax({\n    url: '/synthesis?chan='+$('#chan-cfsm').val(),\n    type: 'POST',\n    data: migoCode(),\n    async: true,\n    success: function(msg) {\n      var obj = JSON.parse(msg);\n      if (obj!=null&&obj.SMC!=null) {\n        writeTo(obj.SMC, '#synthesis-output');\n        $('#synthesis-global').html(obj.Global)\n        $('#synthesis-machines').html(obj.Machines)\n        reportTime(obj.time);\n        $('#synthesis-wrap').addClass('visible');\n      } else {\n        writeTo(\"JSON error\", '#synthesis-output');\n      }\n    }\n  });\n});\n$('#synthesis-output-close').on('click', function() {\n  $('#synthesis-wrap').removeClass('visible');\n})\nwriteTo('// Write Go code here\\n'\n  + 'package main\\n\\n'\n  + 'import \"fmt\"\\n\\n'\n  + 'func main() {\\n'\n  + '    ch := make(chan int)   // Create <b>channel</b> <i>ch</i>\\n'\n  + '    go func(ch chan int) { // Spawn <b>goroutine</b>\\n'\n  + '        ch <- 42           // <b>Send</b> value to <i>ch</i>\\n'\n  + '    }(ch)\\n'\n  + '    fmt.Println(<-ch)      // <b>Recv</b> value from <i>ch</i>\\n'\n  + '}\\n', '#go');\n})()\n"
  },
  {
    "path": "static/style.css",
    "content": "html, body {\n  margin: 0;\n  padding: 0;\n  width: 100%;\n  height: 100%;\n  font-family: 'Roboto', sans-serif;\n}\n\ndiv.gocode,\ndiv.generated {\n  position: relative;\n}\n\ndiv.gocode > div.buttons,\ndiv.generated > div.buttons {\n  position: absolute;\n  bottom: 10px;\n  right: 10px;\n}\n\ndiv.output {\n  opacity: 0.95;\n  -moz-border-radius: 10px;\n  -webkit-border-radius: 10px;\n  border-radius: 10px;\n  padding: 10px;\n  background: #222;\n  color: #fff;\n  width: 50%;\n  right: 0;\n  bottom: 0;\n  position: absolute;\n}\ndiv#synthesis-wrap,\ndiv#gong-wrap {\n  position: absolute;\n  display: none;\n  right: 10px;\n  bottom: 10px;\n}\ndiv#synthesis-wrap.visible,\ndiv#gong-wrap.visible {\n  opacity: 0.95;\n  position: absolute;\n  display: inline-block;\n  right: 10px;\n  bottom: 10px;\n  -moz-border-radius: 10px;\n  -webkit-border-radius: 10px;\n  border-radius: 10px;\n  padding: 10px;\n  background: #222;\n  color: #fff;\n}\n\ndiv.code {\n  white-space: pre;\n  background: #eee;\n  font-family: 'Fira Mono', monospace;\n  font-weight: 400;\n  padding: 20px;\n  -webkit-font-smoothing: antialiased;\n  -webkit-line-break: after-white-space;\n  -webkit-user-modify: read-write;\n}\n\ndiv#go:before {\n  content: 'Go';\n}\ndiv#out:before {\n  content: attr(lang);\n}\ndiv#go:before,\ndiv#out[lang]:before {\n  position: absolute;\n  top: 0;\n  right: 0;\n  background: #888;\n  padding: 10px;\n  color: #fff;\n  opacity: 0.8;\n  font-family: 'Fira Mono', monospace;\n  -webkit-border-bottom-left-radius: 5;\n  -moz-border-bottom-left-radius: 5;\n  border-bottom-left-radius: 5px;\n}\n\ndiv#out:not([lang]) + div.buttons,\ndiv#out[lang] + div.buttons {\n  display: none;\n}\ndiv#out[lang='MiGo'] + div.buttons,\ndiv#out[lang='CFSM'] + div.buttons {\n  display: block;\n}\n#time {\n  font-family: 'Roboto';\n  font-weight: 100;\n  font-size: smaller;\n}\n\ndiv.code pre {\n  font-family: 'Fira Mono', monospace;\n  line-height: 24px;\n  margin: 0px 0px 0px 0px;\n  word-wrap: break-word;\n  overflow-x: visible;\n  overflow-y: visible;\n  -webkit-font-smoothing: antialiased;\n  -webkit-line-break: after-white-space;\n  -webkit-user-modify: read-write;\n}\n\ndiv.code pre span {\n  white-space: pre;\n}\n\ndiv.output div.buttons {\n  position: absolute;\n  bottom: 10px;\n  right: 10px;\n  color: #000;\n}\n\ndiv.controls {\n  width: 100%;\n  background: #ccc;/* url(/static/logo.png) center right no-repeat;*/\n  padding: 10px 0;\n}\n\ndiv.buttons button,\ndiv.controls button,\ndiv.controls select {\n  background: #44b6d0;\n  -webkit-border-radius: 10;\n  -moz-border-radius: 10;\n  border-radius: 10px;\n  color: #ffffff;\n  padding: 5px 10px 5px 10px;\n  text-decoration: none;\n  border-style: solid;\n  border-width: 2px;\n  border-color: #298ba3;\n  margin: 0 10px;\n}\n\n/* Half-buttons */\ndiv.output div.buttons button.close,\ndiv.output div.buttons button.kill,\ndiv.controls .right {\n  -webkit-border-bottom-left-radius: 0;\n  -moz-border-bottom-left-radius: 0;\n  border-bottom-left-radius: 0;\n  -webkit-border-top-left-radius: 0;\n  -moz-border-top-left-radius: 0;\n  border-top-left-radius: 0;\n  margin-left: 0;\n}\ndiv.output div.buttons button.run,\ndiv.output div.buttons button.kill,\ndiv.controls .left {\n  -webkit-border-bottom-right-radius: 0;\n  -moz-border-bottom-right-radius: 0;\n  border-bottom-right-radius: 0;\n  -webkit-border-top-right-radius: 0;\n  -moz-border-top-right-radius: 0;\n  border-top-right-radius: 0;\n  margin-right: 0;\n}\n\ndiv.controls button:hover {\n  background: #28849b;\n  text-decoration: none;\n}\n\ndiv#synthesis-graphics {\n  max-width: 1000px;\n  overflow-x: auto;\n  background: #ccc;\n}\n\ndiv#synthesis-graphics div#synthesis-machines,\ndiv#synthesis-graphics div#synthesis-global {\n  display: table-cell;\n  padding: 5px;\n  vertical-align: middle;\n}\n\n"
  },
  {
    "path": "talks/basic/concurrency.go",
    "content": "// +build OMIT\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc deepThought(replyCh chan int) {\n\ttime.Sleep(75 * time.Millisecond)\n\treplyCh <- 42 // HLsendrecv\n}\n\nfunc main() {\n\tch := make(chan int) // HLmakechan\n\tgo deepThought(ch)   // HLdl\n\tanswer := <-ch       // HLsendrecv\n\tfmt.Printf(\"The answer is %d\\n\", answer)\n}\n\n// END OMIT\n"
  },
  {
    "path": "talks/basic/fanin.go",
    "content": "// +build OMIT\n\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc work(out chan<- int) {\n\tfor {\n\t\tout <- 42\n\t}\n}\nfunc fanin(input1, input2 <-chan int) <-chan int {\n\tc := make(chan int)\n\tgo func() {\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase s := <-input1:\n\t\t\t\tc <- s\n\t\t\tcase s := <-input2:\n\t\t\t\tc <- s\n\t\t\t}\n\t\t}\n\t}()\n\treturn c\n}\n\nfunc main() {\n\tinput1 := make(chan int)\n\tinput2 := make(chan int)\n\tgo work(input1)\n\tgo work(input2)\n\tc := fanin(input1, input2)\n\tfor {\n\t\tfmt.Println(<-c)\n\t}\n}\n"
  },
  {
    "path": "talks/basic/select.go",
    "content": "// +build OMIT\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"time\"\n)\n\nfunc calc(ch chan int) {\n\trand.Seed(time.Now().Unix())\n\tval := rand.Intn(5)\n\ttime.Sleep(time.Duration(val) * time.Microsecond)\n\tch <- val\n}\n\nfunc main() {\n\tch1, ch2 := make(chan int), make(chan int)\n\tgo calc(ch1)\n\tgo calc(ch2)\n\tselect {\n\tcase ans := <-ch1:\n\t\tfmt.Println(\"Answer from ch1: \", ans)\n\tcase ans := <-ch2:\n\t\tfmt.Println(\"Answer from ch2: \", ans)\n\t}\n}\n"
  },
  {
    "path": "talks/conf-cc-2016/deadlock-global.html",
    "content": "<table><tr><td>\nCFSMs are numbered as below\n<ul>\n    <li><b>0</b>. channel ch</li>\n    <li><b>1</b>. channel done</li>\n    <li><b>2</b>. main → done <i>1</i> ? int → done <i>1</i> ? <tt>int</tt> </li>\n    <li><b>3</b>. Send :19 → ch <i>0</i> ! <tt>int</tt></li>\n    <li><b>4</b>. Recv :20 → ch <i>0</i> ? <tt>int</tt> → done <i>1</i> ! <tt>done</tt></li>\n    <li><b>5</b>. Recv :21 → ch <i>0</i> ? <tt>int</tt> → done <i>1</i> ! <tt>done</tt></li>\n</ul>\n</td><td><img src=\"conf-cc-2016/deadlock-global.png\"/></td></tr></table>\n"
  },
  {
    "path": "talks/conf-cc-2016/fanin-global.html",
    "content": "<table><tr><td>\n<div class=\"code playground\" contenteditable=\"true\" spellcheck=\"false\">\n<pre style=\"display: none\"><span>// +build OMIT\n\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc work(out chan&lt;- int) {\n\tfor {\n\t\tout &lt;- 42\n\t}\n}\n</span></pre>\n\n<pre><span num=\"14\">func fanin(input1, input2 &lt;-chan int) &lt;-chan int {</span>\n<span num=\"15\">    c := make(chan int)</span>\n<span num=\"16\">    go func() {</span>\n<span num=\"17\">        for {</span>\n<span num=\"18\">            select {</span>\n<span num=\"19\">            case s := &lt;-input1:</span>\n<span num=\"20\">                c &lt;- s</span>\n<span num=\"21\">            case s := &lt;-input2:</span>\n<span num=\"22\">                c &lt;- s</span>\n<span num=\"23\">            }</span>\n<span num=\"24\">        }</span>\n<span num=\"25\">    }()</span>\n<span num=\"26\">    return c</span>\n<span num=\"27\">}</span>\n</pre>\n\n<pre style=\"display: none\"><span>\nfunc main() {\n\tinput1 := make(chan int)\n\tinput2 := make(chan int)\n\tgo work(input1)\n\tgo work(input2)\n\tc := fanin(input1, input2)\n\tfor {\n\t\tfmt.Println(&lt;-c)\n\t}\n}\n</span></pre>\n</div>\n</td><td><img src=\"conf-cc-2016/fanin-global.png\"/></td></tr></table>\n"
  },
  {
    "path": "talks/conf-cc-2016/select.html",
    "content": "\n<table><tr><td>\n<div class=\"code playground\" contenteditable=\"true\" spellcheck=\"false\" autocomplete=\"off\">\n<pre style=\"display: none\"><span>// +build OMIT\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"time\"\n)\n\nfunc calc(ch chan int) {\n\trand.Seed(time.Now().Unix())\n\tval := rand.Intn(5)\n\ttime.Sleep(time.Duration(val) * time.Microsecond)\n\tch &lt;- val\n}\n\n</span></pre>\n\n<pre><span num=\"18\">func main() {</span>\n<span num=\"19\">    ch1, ch2 := make(chan int), make(chan int)</span>\n<span num=\"20\">    go calc(ch1)</span>\n<span num=\"21\">    go calc(ch2)</span>\n<span num=\"22\">    select {</span>\n<span num=\"23\">    case ans := &lt;-ch1:</span>\n<span num=\"24\">        fmt.Println(\"Answer from ch1: \", ans)</span>\n<span num=\"25\">    case ans := &lt;-ch2:</span>\n<span num=\"26\">        fmt.Println(\"Answer from ch2: \", ans)</span>\n<span num=\"27\">    }</span>\n<span num=\"28\">}</span>\n</pre>\n\n</div>\n</td><td>\n<div class=\"image\"><img src=\"conf-cc-2016/select-cfsm.png\"></div>\n</td></tr></table>\n"
  },
  {
    "path": "talks/conf-cc-2016/ssa.html",
    "content": "<table><tr><td>\n<div class=\"code\" contenteditable=\"true\" spellcheck=\"false\">\n<pre>func main():\n0:                                                    entry P:0 S:0\n    <b>t0 = make chan int 0:int</b>                           chan int\n    <b>t1 = make chan int 0:int</b>                           chan int\n    t2 = changetype chan&lt;- int &lt;- chan int (t0)      chan&lt;- int\n    go Send(t2)\n    t3 = changetype &lt;-chan int &lt;- chan int (t0)      &lt;-chan int\n    t4 = changetype chan&lt;- int &lt;- chan int (t1)      chan&lt;- int\n    go RecvAck(t3, t4)\n    t5 = changetype &lt;-chan int &lt;- chan int (t0)      &lt;-chan int\n    t6 = changetype chan&lt;- int &lt;- chan int (t1)      chan&lt;- int\n    go RecvAck(t5, t6)\n    go main$1()\n    <b>t7 = &lt;-t1</b>                                               int\n    <b>t8 = &lt;-t1</b>                                               int\n    return\n</pre>\n</div>\n</td>\n<td>\n<div class=\"image\"><img src=\"conf-cc-2016/ssa.png\"></div>\n</td></tr></table>\n"
  },
  {
    "path": "talks/conf-cc-2016/type-inference.html",
    "content": "<table><tr><td>\n<div class=\"code\" contenteditable=\"true\" spellcheck=\"false\">\n<pre><span num=\"17\">func main() {</span>\n<span num=\"18\">    ch, done := make(chan int), make(chan int)</span>\n<span num=\"19\">    go Send(ch)</span>\n<span num=\"20\">    go RecvAck(ch, done)</span>\n<span num=\"21\">    go RecvAck(ch, done) // OOPS</span>\n<span num=\"22\"></span>\n<span num=\"23\">    // Anonymous goroutine: Some long running work (e.g. http service)</span>\n<span num=\"24\">    go func() {</span>\n<span num=\"25\">        for i := 0; i &lt; 3; i++ {</span>\n<span num=\"26\">            fmt.Println(\"Working #\", i); time.Sleep(1 * time.Second)</span>\n<span num=\"27\">        }</span>\n<span num=\"28\">    }()</span>\n<span num=\"29\">    result = &lt;-done</span>\n<span num=\"30\">    result = &lt;-done // OOPS</span>\n<span num=\"31\">    fmt.Println(\"Result is \", result)</span>\n<span num=\"32\">}</span>\n</pre>\n</div>\n</td>\n<td>\n<div class=\"image\"><img src=\"conf-cc-2016/type-inference.png\"></div>\n</td></tr></table>\n"
  },
  {
    "path": "talks/conf-cc-2016.slide",
    "content": "Static Deadlock Detection for Go by Global Session Graph Synthesis\n\n*Nicholas*Ng* & Nobuko Yoshida\n\nDepartment of Computing\nImperial College London\n{nickng,n.yoshida}@imperial.ac.uk\n\nhttp://mrg.doc.ic.ac.uk\n\n* Contributions\n\n- Static deadlock detection tool _dingo-hunter_\n- Deadlock detection based on session types\n- *Infer* session types as Communicating Automata\n- *Synthesise* global session graphs from CA\n\n.image conf-cc-2016/overview.png\n\n* Go and Concurrency\n\n- Developed by Google for multi-core programming\n- Concurrency model built on CSP (process calculi)\n- Message-passing *communication* over channels\n\n\" _Do_not_communicate_by_sharing_memory_; _instead_, _share_memory_by_communicating_. \"\n-- Effective Go (developer guide)\n\n* Go concurrency: Goroutines and Channels\n\n- Goroutines: lightweight threads\n- Channels: bounded queues (default size 0)\n- Go concurrency = compose goroutines & coordinate with channels\n\n.play basic/concurrency.go /^func deepThought/,/END OMIT$/ HLsendrecv\n\n* Concurrency Problems\n\n- Deadlocks [[#colour:red][☹]]\n- _Some_ goroutines are blocked waiting forever\n\n.play basic/concurrency.go /^func deepThought/,/END OMIT$/ HLdl\n\n* fatal error: all goroutines are asleep - deadlock!\n\n- Go has a _runtime_ [[http://github.com/golang/go/blob/d2c81ad84776edfb4c790666f1d80554b4393d46/src/runtime/proc.go#L3243][deadlock detector]]\n- No. of running goroutines:  if `run`>`0` ⟶ OK [[#colour:green][✓]]; if `run`==`0` ⟶ deadlock [[#colour:red][☹]]\n\n.code snippet/proc.txt\n\n* Runtime deadlock detection\n\n- No false positive\n- Only if deadlock during execution\n- Only *global* deadlocks: if ALL goroutines are blocked\n\n* Defeating runtime deadlock detection\n\n.play deadlock/deadlock.go /^func Send/,/^}/ HLoops\n\nThis will be our running example\n\n* Static Analysis & Deadlock Detection by Global Graph Synthesis\n\n* Static Analysis & Deadlock Detection by Global Graph Synthesis\n\n.image conf-cc-2016/overview.png\n\n- Explore all branches (vs. only execution path for runtime checker)\n- Approach based on *Multiparty*Session*Types* (MPST) [1]\n- Guarantee communication-safety & *deadlock-freedom* for n-party interactions\n.caption [1]: Honda, Yoshida, Carbone, _Multiparty_Asynchronous_Session_Types_ , POPL'08, J. ACM\n\n* The Multiparty Session Types (MPST) framework\n\n.image conf-cc-2016/mpst.png\n\n- *Global*Graph*Synthesis* [2] [[#colour:blue][local type(s)]] ⟶ [[#colour:red][global type]]\n- [[#colour:blue][Local types]] as _Communicating_Automata_ [3]\n- [[#colour:red][Global types]] as _graphical_choreographies_\n- Safety guaranteed by *multiparty*compatibility* property (global)\n\n.caption [2]: Lange, Tuosto, Yoshida, _From_Communicating_Machines_to_Graphical_Choreographies_ , POPL'15\n.caption [3]: Brand, Zafiropulo, _On_communicating_finite-state_machines_ , J.  ACM Vol. 30 No. 2, 1983\n\n* Type inference from Go code\n\n- Control flow graph as Finite State Machine (FSM), per goroutine\n- + Communication primitives: `make(chan`T)`, send, receive\n\n.html conf-cc-2016/type-inference.html\n\n* Inferred local session types\n\n- For simplicity: real variable names\n- Note: `main$1:24` (work anonymous function) has no communication\n.image conf-cc-2016/local-type.png\n.caption Variable names: `ch`=`main.t0`, `done`=`main.t1`\n\n* Inferred Local Types to Goroutine Automata\n\n- Local session types ⟹ _Communicating_Automata_\n- *Receive* is ? transition, *Send* is ! transition\n\n.image conf-cc-2016/cfsm.png\n\n#.caption [4]: Deniélou, Yoshida, _Multiparty_Compatibility_in_Communicating_Automata:_Characterisation_and_Synthesis_ , ICALP 2013\n\n* Channel Automata\n\n- Channels in *Communicating*Automata* - _fixed_ point-to-point links\n- *Go*channels* - _shared_, do not represent _fixed_ endpoint\n- 2 goroutines writing to same channel valid [[#colour:green][✓]]\n\n.image conf-cc-2016/channel-problem.png\n.caption Note: Go channels valid regardless of the deadlock\n\n* Channel Automata\n\n- *Channel*Automata* defers selection to a machine (for each channel)\n- *Unbuffered*channel* q0 → receive → send → q0\n\n.image conf-cc-2016/channel-cfsm.png\n.caption Channel Automata _done_ & _ch_ (only essential transitions)\n\n* Bonus: Select non-deterministic choice\n\n- Switch-case for communication\n\n.html conf-cc-2016/select.html\n\n* Global Graph Synthesis\n\n- Join both automata to get overall *global* graph\n- All synchronous transitions, [[#colour:blue][A: `ch!int`]] + [[#colour:blue][B: `ch?int`]] becomes [[#colour:red][A → B: int]]\n\n.image conf-cc-2016/global.png\n\n* Global Graph Synthesis: What is safe?\n\n- *Multiparty*Compatibility* property on global graph\n- *Representability* All [Goroutine] automata are \"represented\" in global graph\n- *Branching*condition* Branches are propagated to all machines in choice\n\n.image conf-cc-2016/compatibility.png\n\n* Fan-in pattern\n\n- Merging pattern with `select`\n.html conf-cc-2016/fanin-global.html\n\n* Bigger example: htcat - Concurrent HTTP GETs\n\n.image conf-cc-2016/htcat-snippet.png\n.caption [[https://github.com/htcat/htcat][github.com:htcat/htcat]] (721 LoC)\n\n* Bigger example: htcat - Concurrent HTTP GETs\n\n#.background conf-cc-2016/htcat.svg\n\n- 7xx lines\n- 9632 nodes\n- 11 Goroutine Automata / 8 Channel Automata\n- Safe [[#colour:green][✓]]\n- Error handling code: conditional new goroutine\n\n    if err != nil {\n        go cat.d.cancel(err)\n        return\n    }\n\n* Conclusion\n\n- Static analysis tool for Go\n- Detect deadlock through global session graph synthesis\n- Applied to open source code base\n\nStatic analysis tool\n\n- [[https://github.com/nickng/dingo-hunter][github.com/nickng/dingo-hunter]]\n\nSynthesis tool (POPL'15 artifact)\n\n- [[https://bitbucket.org/julien-lange/gmc-synthesis][bitbucket.org/julien-lange/gmc-synthesis]]\n\n.image conf-cc-2016/hunter.png\n\n* Future Work\n\n- *Buffered*channels* asynchronous communication over channels\n- *Dynamic*patterns* Expand bounded ranges (e.g. `for`i:=0;`i<10;`i++`{...}`)\n\n.image conf-cc-2016/overview.png\n"
  },
  {
    "path": "talks/deadlock/deadlock.go",
    "content": "// +build OMIT\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nvar (\n\tresult int\n)\n\nfunc Send(ch chan<- int)                     { ch <- 42 }             // Send\nfunc RecvAck(ch <-chan int, done chan<- int) { v := <-ch; done <- v } // Recv then Send\n\nfunc main() {\n\tch, done := make(chan int), make(chan int)\n\tgo Send(ch)\n\tgo RecvAck(ch, done)\n\tgo RecvAck(ch, done) // OOPS // HLoops\n\n\t// Anonymous goroutine: Some long running work (e.g. http service)\n\tgo func() {\n\t\tfor i := 0; i < 3; i++ {\n\t\t\tfmt.Println(\"Working #\", i)\n\t\t\ttime.Sleep(1 * time.Second)\n\t\t}\n\t}()\n\tresult = <-done\n\tresult = <-done // OOPS // HLoops\n\tfmt.Println(\"Result is \", result)\n}\n"
  },
  {
    "path": "talks/snippet/proc.txt",
    "content": "// From Go source code - https://github.com/golang/go\n// File runtime/proc.go\n\n// +build OMIT\n\nfunc checkdead() {\n...\n\t// -1 for sysmon\n\trun := sched.mcount - sched.nmidle - sched.nmidlelocked - 1 // HL\n\tif run > 0 {\n\t\treturn\n\t}\n...\n\tgetg().m.throwing = -1 // do not dump full stacks\n\tthrow(\"all goroutines are asleep - deadlock!\")\n}\n"
  },
  {
    "path": "talks/static/article.css",
    "content": "body {\n\tmargin: 0;\n\tfont-family: Helvetica, Arial, sans-serif;\n\tfont-size: 16px;\n}\npre,\ncode {\n\tfont-family: Menlo, monospace;\n\tfont-size: 14px;\n}\npre {\n\tline-height: 18px;\n\tmargin: 0;\n\tpadding: 0;\n}\na {\n\tcolor: #375EAB;\n\ttext-decoration: none;\n}\na:hover {\n\ttext-decoration: underline;\n}\np, ul, ol {\n\tmargin: 20px;\n}\n\nh1, h2, h3, h4 {\n\tmargin: 20px 0;\n\tpadding: 0;\n\tcolor: #375EAB;\n\tfont-weight: bold;\n}\nh1 {\n\tfont-size: 24px;\n}\nh2 {\n\tfont-size: 20px;\n\tbackground: #E0EBF5;\n\tpadding: 2px 5px;\n}\nh3 {\n\tfont-size: 20px;\n}\nh3, h4 {\n\tmargin: 20px 5px;\n}\nh4 {\n\tfont-size: 16px;\n}\n\ndiv#heading {\n\tfloat: left;\n\tmargin: 0 0 10px 0;\n\tpadding: 21px 0;\n\tfont-size: 20px;\n\tfont-weight: normal;\n}\n\ndiv#topbar {\n\tbackground: #E0EBF5;\n\theight: 64px;\n\toverflow: hidden;\n}\n\nbody {\n\ttext-align: center;\n}\ndiv#page {\n\twidth: 100%;\n}\ndiv#page > .container,\ndiv#topbar > .container {\n\ttext-align: left;\n\tmargin-left: auto;\n\tmargin-right: auto;\n\tpadding: 0 20px;\n\twidth: 900px;\n}\ndiv#page.wide > .container,\ndiv#topbar.wide > .container {\n\twidth: auto;\n}\n\ndiv#footer {\n\ttext-align: center;\n\tcolor: #666;\n\tfont-size: 14px;\n\tmargin: 40px 0;\n}\n\n.author p {\n\tmargin: 20, 0, 0, 0px;\n}\n\ndiv.code,\ndiv.output {\n\tmargin: 20px;\n\tpadding: 10px;\n\t-webkit-border-radius: 5px;\n\t-moz-border-radius: 5px;\n\tborder-radius: 5px;\n}\n\ndiv.code { background: #e9e9e9; }\ndiv.output { background: black; }\ndiv.output .stdout { color: #e6e6e6; }\ndiv.output .stderr { color: rgb(244, 74, 63); }\ndiv.output .system { color: rgb(255, 209, 77) }\n\n.buttons {\n\tmargin-left: 20px;\n}\ndiv.output .buttons {\n\tmargin-left: 0;\n\tmargin-bottom: 10px;\n}\n\n#toc {\n\tfloat: right;\n\tmargin: 0px 10px;\n\tpadding: 10px;\n\tborder: 1px solid #e5ecf9; \n\tbackground-color: white;\n\tmax-width: 33%;\n\n\t-webkit-border-radius: 5px;\n\t-moz-border-radius: 5px;\n\tborder-radius: 5px;\n}\n\n#toc ul, #toc a {\n\tlist-style-type: none;\n\tpadding-left: 10px;\n\tcolor: black;\n\tmargin: 0px;\n}\n"
  },
  {
    "path": "talks/static/dir.css",
    "content": "/* copied from $GOROOT/doc/style.css */\n\nbody {\n\tmargin: 0;\n\tfont-family: Helvetica, Arial, sans-serif;\n\tfont-size: 16px;\n}\npre,\ncode {\n\tfont-family: Menlo, monospace;\n\tfont-size: 14px;\n}\npre {\n\tline-height: 18px;\n}\npre .comment {\n\tcolor: #375EAB;\n}\npre .highlight,\npre .highlight-comment,\npre .selection-highlight,\npre .selection-highlight-comment {\n\tbackground: #FFFF00;\n}\npre .selection,\npre .selection-comment {\n\tbackground: #FF9632;\n}\npre .ln {\n\tcolor: #999;\n}\nbody {\n\tcolor: #222;\n}\na,\n.exampleHeading .text {\n\tcolor: #375EAB;\n\ttext-decoration: none;\n}\na:hover,\n.exampleHeading .text:hover {\n\ttext-decoration: underline;\n}\np,\npre,\nul,\nol {\n\tmargin: 20px;\n}\npre {\n\tbackground: #e9e9e9;\n\tpadding: 10px;\n\n\t-webkit-border-radius: 5px;\n\t-moz-border-radius: 5px;\n\tborder-radius: 5px;\n}\n\nh1,\nh2,\nh3,\nh4,\n.rootHeading {\n\tmargin: 20px 0;\n\tpadding: 0;\n\tcolor: #375EAB;\n\tfont-weight: bold;\n}\nh1 {\n\tfont-size: 24px;\n}\nh2 {\n\tfont-size: 20px;\n\tbackground: #E0EBF5;\n\tpadding: 2px 5px;\n}\nh3 {\n\tfont-size: 20px;\n}\nh3,\nh4 {\n\tmargin: 20px 5px;\n}\nh4 {\n\tfont-size: 16px;\n}\n\ndl {\n\tmargin: 20px;\n}\ndd {\n\tmargin: 2px 20px;\n}\ndl,\ndd {\n\tfont-size: 14px;\n}\ndiv#nav table td {\n\tvertical-align: top;\n}\n\ndiv#heading {\n\tfloat: left;\n\tmargin: 0 0 10px 0;\n\tpadding: 21px 0;\n\tfont-size: 20px;\n\tfont-weight: normal;\n}\ndiv#heading a {\n\tcolor: #222;\n\ttext-decoration: none;\n}\n\ndiv#topbar {\n\tbackground: #E0EBF5;\n\theight: 64px;\n}\n\nbody {\n\ttext-align: center;\n}\ndiv#page,\ndiv#topbar > .container {\n\tclear: both;\n\ttext-align: left;\n\tmargin-left: auto;\n\tmargin-right: auto;\n\tpadding: 0 20px;\n\twidth: 900px;\n}\ndiv#page.wide,\ndiv#topbar > .wide {\n\twidth: auto;\n}\ndiv#plusone {\n\tfloat: right;\n}\n\ndiv#footer {\n\tcolor: #666;\n\tfont-size: 14px;\n\tmargin: 40px 0;\n}\n\ndiv#menu > a,\ndiv#menu > input {\n\tpadding: 10px;\n\n\ttext-decoration: none;\n\tfont-size: 16px;\n\n\t-webkit-border-radius: 5px;\n\t-moz-border-radius: 5px;\n\tborder-radius: 5px;\n}\ndiv#menu > a,\ndiv#menu > input {\n\tborder: 1px solid #375EAB;\n}\ndiv#menu > a {\n\tcolor: white;\n\tbackground: #375EAB;\n}\n\ndiv#menu {\n\tfloat: right;\n\tmin-width: 590px;\n\tpadding: 10px 0;\n\ttext-align: right;\n}\ndiv#menu > a {\n\tmargin-right: 5px;\n\tmargin-bottom: 10px;\n\n\tpadding: 10px;\n}\ndiv#menu > input {\n\tposition: relative;\n\ttop: 1px;\n\twidth: 60px;\n\tbackground: white;\n\tcolor: #222;\n}\ndiv#menu > input.inactive {\n\tcolor: #999;\n}\n"
  },
  {
    "path": "talks/static/dir.js",
    "content": "// Copyright 2012 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// copied from $GOROOT/doc/godocs.js\n\nfunction bindEvent(el, e, fn) {\n  if (el.addEventListener){\n    el.addEventListener(e, fn, false);\n  } else if (el.attachEvent){\n    el.attachEvent('on'+e, fn);\n  }\n}\n\nfunction godocs_bindSearchEvents() {\n  var search = document.getElementById('search');\n  if (!search) {\n    // no search box (index disabled)\n    return;\n  }\n  function clearInactive() {\n    if (search.className == \"inactive\") {\n      search.value = \"\";\n      search.className = \"\";\n    }\n  }\n  function restoreInactive() {\n    if (search.value !== \"\") {\n      return;\n    }\n    if (search.type != \"search\") {\n      search.value = search.getAttribute(\"placeholder\");\n    }\n    search.className = \"inactive\";\n  }\n  restoreInactive();\n  bindEvent(search, 'focus', clearInactive);\n  bindEvent(search, 'blur', restoreInactive);\n}\n\nbindEvent(window, 'load', godocs_bindSearchEvents);\n"
  },
  {
    "path": "talks/static/jquery-ui.js",
    "content": "/*! jQuery UI - v1.10.2 - 2013-03-20\n* http://jqueryui.com\n* Includes: jquery.ui.core.js, jquery.ui.widget.js, jquery.ui.mouse.js, jquery.ui.resizable.js\n* Copyright 2013 jQuery Foundation and other contributors Licensed MIT */\n\n(function(e,t){function i(t,i){var a,n,r,o=t.nodeName.toLowerCase();return\"area\"===o?(a=t.parentNode,n=a.name,t.href&&n&&\"map\"===a.nodeName.toLowerCase()?(r=e(\"img[usemap=#\"+n+\"]\")[0],!!r&&s(r)):!1):(/input|select|textarea|button|object/.test(o)?!t.disabled:\"a\"===o?t.href||i:i)&&s(t)}function s(t){return e.expr.filters.visible(t)&&!e(t).parents().addBack().filter(function(){return\"hidden\"===e.css(this,\"visibility\")}).length}var a=0,n=/^ui-id-\\d+$/;e.ui=e.ui||{},e.extend(e.ui,{version:\"1.10.2\",keyCode:{BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38}}),e.fn.extend({focus:function(t){return function(i,s){return\"number\"==typeof i?this.each(function(){var t=this;setTimeout(function(){e(t).focus(),s&&s.call(t)},i)}):t.apply(this,arguments)}}(e.fn.focus),scrollParent:function(){var t;return t=e.ui.ie&&/(static|relative)/.test(this.css(\"position\"))||/absolute/.test(this.css(\"position\"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(e.css(this,\"position\"))&&/(auto|scroll)/.test(e.css(this,\"overflow\")+e.css(this,\"overflow-y\")+e.css(this,\"overflow-x\"))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(e.css(this,\"overflow\")+e.css(this,\"overflow-y\")+e.css(this,\"overflow-x\"))}).eq(0),/fixed/.test(this.css(\"position\"))||!t.length?e(document):t},zIndex:function(i){if(i!==t)return this.css(\"zIndex\",i);if(this.length)for(var s,a,n=e(this[0]);n.length&&n[0]!==document;){if(s=n.css(\"position\"),(\"absolute\"===s||\"relative\"===s||\"fixed\"===s)&&(a=parseInt(n.css(\"zIndex\"),10),!isNaN(a)&&0!==a))return a;n=n.parent()}return 0},uniqueId:function(){return this.each(function(){this.id||(this.id=\"ui-id-\"+ ++a)})},removeUniqueId:function(){return this.each(function(){n.test(this.id)&&e(this).removeAttr(\"id\")})}}),e.extend(e.expr[\":\"],{data:e.expr.createPseudo?e.expr.createPseudo(function(t){return function(i){return!!e.data(i,t)}}):function(t,i,s){return!!e.data(t,s[3])},focusable:function(t){return i(t,!isNaN(e.attr(t,\"tabindex\")))},tabbable:function(t){var s=e.attr(t,\"tabindex\"),a=isNaN(s);return(a||s>=0)&&i(t,!a)}}),e(\"<a>\").outerWidth(1).jquery||e.each([\"Width\",\"Height\"],function(i,s){function a(t,i,s,a){return e.each(n,function(){i-=parseFloat(e.css(t,\"padding\"+this))||0,s&&(i-=parseFloat(e.css(t,\"border\"+this+\"Width\"))||0),a&&(i-=parseFloat(e.css(t,\"margin\"+this))||0)}),i}var n=\"Width\"===s?[\"Left\",\"Right\"]:[\"Top\",\"Bottom\"],r=s.toLowerCase(),o={innerWidth:e.fn.innerWidth,innerHeight:e.fn.innerHeight,outerWidth:e.fn.outerWidth,outerHeight:e.fn.outerHeight};e.fn[\"inner\"+s]=function(i){return i===t?o[\"inner\"+s].call(this):this.each(function(){e(this).css(r,a(this,i)+\"px\")})},e.fn[\"outer\"+s]=function(t,i){return\"number\"!=typeof t?o[\"outer\"+s].call(this,t):this.each(function(){e(this).css(r,a(this,t,!0,i)+\"px\")})}}),e.fn.addBack||(e.fn.addBack=function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}),e(\"<a>\").data(\"a-b\",\"a\").removeData(\"a-b\").data(\"a-b\")&&(e.fn.removeData=function(t){return function(i){return arguments.length?t.call(this,e.camelCase(i)):t.call(this)}}(e.fn.removeData)),e.ui.ie=!!/msie [\\w.]+/.exec(navigator.userAgent.toLowerCase()),e.support.selectstart=\"onselectstart\"in document.createElement(\"div\"),e.fn.extend({disableSelection:function(){return this.bind((e.support.selectstart?\"selectstart\":\"mousedown\")+\".ui-disableSelection\",function(e){e.preventDefault()})},enableSelection:function(){return this.unbind(\".ui-disableSelection\")}}),e.extend(e.ui,{plugin:{add:function(t,i,s){var a,n=e.ui[t].prototype;for(a in s)n.plugins[a]=n.plugins[a]||[],n.plugins[a].push([i,s[a]])},call:function(e,t,i){var s,a=e.plugins[t];if(a&&e.element[0].parentNode&&11!==e.element[0].parentNode.nodeType)for(s=0;a.length>s;s++)e.options[a[s][0]]&&a[s][1].apply(e.element,i)}},hasScroll:function(t,i){if(\"hidden\"===e(t).css(\"overflow\"))return!1;var s=i&&\"left\"===i?\"scrollLeft\":\"scrollTop\",a=!1;return t[s]>0?!0:(t[s]=1,a=t[s]>0,t[s]=0,a)}})})(jQuery);(function(e,t){var i=0,s=Array.prototype.slice,n=e.cleanData;e.cleanData=function(t){for(var i,s=0;null!=(i=t[s]);s++)try{e(i).triggerHandler(\"remove\")}catch(a){}n(t)},e.widget=function(i,s,n){var a,r,o,h,l={},u=i.split(\".\")[0];i=i.split(\".\")[1],a=u+\"-\"+i,n||(n=s,s=e.Widget),e.expr[\":\"][a.toLowerCase()]=function(t){return!!e.data(t,a)},e[u]=e[u]||{},r=e[u][i],o=e[u][i]=function(e,i){return this._createWidget?(arguments.length&&this._createWidget(e,i),t):new o(e,i)},e.extend(o,r,{version:n.version,_proto:e.extend({},n),_childConstructors:[]}),h=new s,h.options=e.widget.extend({},h.options),e.each(n,function(i,n){return e.isFunction(n)?(l[i]=function(){var e=function(){return s.prototype[i].apply(this,arguments)},t=function(e){return s.prototype[i].apply(this,e)};return function(){var i,s=this._super,a=this._superApply;return this._super=e,this._superApply=t,i=n.apply(this,arguments),this._super=s,this._superApply=a,i}}(),t):(l[i]=n,t)}),o.prototype=e.widget.extend(h,{widgetEventPrefix:r?h.widgetEventPrefix:i},l,{constructor:o,namespace:u,widgetName:i,widgetFullName:a}),r?(e.each(r._childConstructors,function(t,i){var s=i.prototype;e.widget(s.namespace+\".\"+s.widgetName,o,i._proto)}),delete r._childConstructors):s._childConstructors.push(o),e.widget.bridge(i,o)},e.widget.extend=function(i){for(var n,a,r=s.call(arguments,1),o=0,h=r.length;h>o;o++)for(n in r[o])a=r[o][n],r[o].hasOwnProperty(n)&&a!==t&&(i[n]=e.isPlainObject(a)?e.isPlainObject(i[n])?e.widget.extend({},i[n],a):e.widget.extend({},a):a);return i},e.widget.bridge=function(i,n){var a=n.prototype.widgetFullName||i;e.fn[i]=function(r){var o=\"string\"==typeof r,h=s.call(arguments,1),l=this;return r=!o&&h.length?e.widget.extend.apply(null,[r].concat(h)):r,o?this.each(function(){var s,n=e.data(this,a);return n?e.isFunction(n[r])&&\"_\"!==r.charAt(0)?(s=n[r].apply(n,h),s!==n&&s!==t?(l=s&&s.jquery?l.pushStack(s.get()):s,!1):t):e.error(\"no such method '\"+r+\"' for \"+i+\" widget instance\"):e.error(\"cannot call methods on \"+i+\" prior to initialization; \"+\"attempted to call method '\"+r+\"'\")}):this.each(function(){var t=e.data(this,a);t?t.option(r||{})._init():e.data(this,a,new n(r,this))}),l}},e.Widget=function(){},e.Widget._childConstructors=[],e.Widget.prototype={widgetName:\"widget\",widgetEventPrefix:\"\",defaultElement:\"<div>\",options:{disabled:!1,create:null},_createWidget:function(t,s){s=e(s||this.defaultElement||this)[0],this.element=e(s),this.uuid=i++,this.eventNamespace=\".\"+this.widgetName+this.uuid,this.options=e.widget.extend({},this.options,this._getCreateOptions(),t),this.bindings=e(),this.hoverable=e(),this.focusable=e(),s!==this&&(e.data(s,this.widgetFullName,this),this._on(!0,this.element,{remove:function(e){e.target===s&&this.destroy()}}),this.document=e(s.style?s.ownerDocument:s.document||s),this.window=e(this.document[0].defaultView||this.document[0].parentWindow)),this._create(),this._trigger(\"create\",null,this._getCreateEventData()),this._init()},_getCreateOptions:e.noop,_getCreateEventData:e.noop,_create:e.noop,_init:e.noop,destroy:function(){this._destroy(),this.element.unbind(this.eventNamespace).removeData(this.widgetName).removeData(this.widgetFullName).removeData(e.camelCase(this.widgetFullName)),this.widget().unbind(this.eventNamespace).removeAttr(\"aria-disabled\").removeClass(this.widgetFullName+\"-disabled \"+\"ui-state-disabled\"),this.bindings.unbind(this.eventNamespace),this.hoverable.removeClass(\"ui-state-hover\"),this.focusable.removeClass(\"ui-state-focus\")},_destroy:e.noop,widget:function(){return this.element},option:function(i,s){var n,a,r,o=i;if(0===arguments.length)return e.widget.extend({},this.options);if(\"string\"==typeof i)if(o={},n=i.split(\".\"),i=n.shift(),n.length){for(a=o[i]=e.widget.extend({},this.options[i]),r=0;n.length-1>r;r++)a[n[r]]=a[n[r]]||{},a=a[n[r]];if(i=n.pop(),s===t)return a[i]===t?null:a[i];a[i]=s}else{if(s===t)return this.options[i]===t?null:this.options[i];o[i]=s}return this._setOptions(o),this},_setOptions:function(e){var t;for(t in e)this._setOption(t,e[t]);return this},_setOption:function(e,t){return this.options[e]=t,\"disabled\"===e&&(this.widget().toggleClass(this.widgetFullName+\"-disabled ui-state-disabled\",!!t).attr(\"aria-disabled\",t),this.hoverable.removeClass(\"ui-state-hover\"),this.focusable.removeClass(\"ui-state-focus\")),this},enable:function(){return this._setOption(\"disabled\",!1)},disable:function(){return this._setOption(\"disabled\",!0)},_on:function(i,s,n){var a,r=this;\"boolean\"!=typeof i&&(n=s,s=i,i=!1),n?(s=a=e(s),this.bindings=this.bindings.add(s)):(n=s,s=this.element,a=this.widget()),e.each(n,function(n,o){function h(){return i||r.options.disabled!==!0&&!e(this).hasClass(\"ui-state-disabled\")?(\"string\"==typeof o?r[o]:o).apply(r,arguments):t}\"string\"!=typeof o&&(h.guid=o.guid=o.guid||h.guid||e.guid++);var l=n.match(/^(\\w+)\\s*(.*)$/),u=l[1]+r.eventNamespace,c=l[2];c?a.delegate(c,u,h):s.bind(u,h)})},_off:function(e,t){t=(t||\"\").split(\" \").join(this.eventNamespace+\" \")+this.eventNamespace,e.unbind(t).undelegate(t)},_delay:function(e,t){function i(){return(\"string\"==typeof e?s[e]:e).apply(s,arguments)}var s=this;return setTimeout(i,t||0)},_hoverable:function(t){this.hoverable=this.hoverable.add(t),this._on(t,{mouseenter:function(t){e(t.currentTarget).addClass(\"ui-state-hover\")},mouseleave:function(t){e(t.currentTarget).removeClass(\"ui-state-hover\")}})},_focusable:function(t){this.focusable=this.focusable.add(t),this._on(t,{focusin:function(t){e(t.currentTarget).addClass(\"ui-state-focus\")},focusout:function(t){e(t.currentTarget).removeClass(\"ui-state-focus\")}})},_trigger:function(t,i,s){var n,a,r=this.options[t];if(s=s||{},i=e.Event(i),i.type=(t===this.widgetEventPrefix?t:this.widgetEventPrefix+t).toLowerCase(),i.target=this.element[0],a=i.originalEvent)for(n in a)n in i||(i[n]=a[n]);return this.element.trigger(i,s),!(e.isFunction(r)&&r.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},e.each({show:\"fadeIn\",hide:\"fadeOut\"},function(t,i){e.Widget.prototype[\"_\"+t]=function(s,n,a){\"string\"==typeof n&&(n={effect:n});var r,o=n?n===!0||\"number\"==typeof n?i:n.effect||i:t;n=n||{},\"number\"==typeof n&&(n={duration:n}),r=!e.isEmptyObject(n),n.complete=a,n.delay&&s.delay(n.delay),r&&e.effects&&e.effects.effect[o]?s[t](n):o!==t&&s[o]?s[o](n.duration,n.easing,a):s.queue(function(i){e(this)[t](),a&&a.call(s[0]),i()})}})})(jQuery);(function(e){var t=!1;e(document).mouseup(function(){t=!1}),e.widget(\"ui.mouse\",{version:\"1.10.2\",options:{cancel:\"input,textarea,button,select,option\",distance:1,delay:0},_mouseInit:function(){var t=this;this.element.bind(\"mousedown.\"+this.widgetName,function(e){return t._mouseDown(e)}).bind(\"click.\"+this.widgetName,function(i){return!0===e.data(i.target,t.widgetName+\".preventClickEvent\")?(e.removeData(i.target,t.widgetName+\".preventClickEvent\"),i.stopImmediatePropagation(),!1):undefined}),this.started=!1},_mouseDestroy:function(){this.element.unbind(\".\"+this.widgetName),this._mouseMoveDelegate&&e(document).unbind(\"mousemove.\"+this.widgetName,this._mouseMoveDelegate).unbind(\"mouseup.\"+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(i){if(!t){this._mouseStarted&&this._mouseUp(i),this._mouseDownEvent=i;var s=this,n=1===i.which,a=\"string\"==typeof this.options.cancel&&i.target.nodeName?e(i.target).closest(this.options.cancel).length:!1;return n&&!a&&this._mouseCapture(i)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){s.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(i)&&this._mouseDelayMet(i)&&(this._mouseStarted=this._mouseStart(i)!==!1,!this._mouseStarted)?(i.preventDefault(),!0):(!0===e.data(i.target,this.widgetName+\".preventClickEvent\")&&e.removeData(i.target,this.widgetName+\".preventClickEvent\"),this._mouseMoveDelegate=function(e){return s._mouseMove(e)},this._mouseUpDelegate=function(e){return s._mouseUp(e)},e(document).bind(\"mousemove.\"+this.widgetName,this._mouseMoveDelegate).bind(\"mouseup.\"+this.widgetName,this._mouseUpDelegate),i.preventDefault(),t=!0,!0)):!0}},_mouseMove:function(t){return e.ui.ie&&(!document.documentMode||9>document.documentMode)&&!t.button?this._mouseUp(t):this._mouseStarted?(this._mouseDrag(t),t.preventDefault()):(this._mouseDistanceMet(t)&&this._mouseDelayMet(t)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,t)!==!1,this._mouseStarted?this._mouseDrag(t):this._mouseUp(t)),!this._mouseStarted)},_mouseUp:function(t){return e(document).unbind(\"mousemove.\"+this.widgetName,this._mouseMoveDelegate).unbind(\"mouseup.\"+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,t.target===this._mouseDownEvent.target&&e.data(t.target,this.widgetName+\".preventClickEvent\",!0),this._mouseStop(t)),!1},_mouseDistanceMet:function(e){return Math.max(Math.abs(this._mouseDownEvent.pageX-e.pageX),Math.abs(this._mouseDownEvent.pageY-e.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}})})(jQuery);(function(e){function t(e){return parseInt(e,10)||0}function i(e){return!isNaN(parseInt(e,10))}e.widget(\"ui.resizable\",e.ui.mouse,{version:\"1.10.2\",widgetEventPrefix:\"resize\",options:{alsoResize:!1,animate:!1,animateDuration:\"slow\",animateEasing:\"swing\",aspectRatio:!1,autoHide:!1,containment:!1,ghost:!1,grid:!1,handles:\"e,s,se\",helper:!1,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:90,resize:null,start:null,stop:null},_create:function(){var t,i,s,n,a,o=this,r=this.options;if(this.element.addClass(\"ui-resizable\"),e.extend(this,{_aspectRatio:!!r.aspectRatio,aspectRatio:r.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:r.helper||r.ghost||r.animate?r.helper||\"ui-resizable-helper\":null}),this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)&&(this.element.wrap(e(\"<div class='ui-wrapper' style='overflow: hidden;'></div>\").css({position:this.element.css(\"position\"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css(\"top\"),left:this.element.css(\"left\")})),this.element=this.element.parent().data(\"ui-resizable\",this.element.data(\"ui-resizable\")),this.elementIsWrapper=!0,this.element.css({marginLeft:this.originalElement.css(\"marginLeft\"),marginTop:this.originalElement.css(\"marginTop\"),marginRight:this.originalElement.css(\"marginRight\"),marginBottom:this.originalElement.css(\"marginBottom\")}),this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0}),this.originalResizeStyle=this.originalElement.css(\"resize\"),this.originalElement.css(\"resize\",\"none\"),this._proportionallyResizeElements.push(this.originalElement.css({position:\"static\",zoom:1,display:\"block\"})),this.originalElement.css({margin:this.originalElement.css(\"margin\")}),this._proportionallyResize()),this.handles=r.handles||(e(\".ui-resizable-handle\",this.element).length?{n:\".ui-resizable-n\",e:\".ui-resizable-e\",s:\".ui-resizable-s\",w:\".ui-resizable-w\",se:\".ui-resizable-se\",sw:\".ui-resizable-sw\",ne:\".ui-resizable-ne\",nw:\".ui-resizable-nw\"}:\"e,s,se\"),this.handles.constructor===String)for(\"all\"===this.handles&&(this.handles=\"n,e,s,w,se,sw,ne,nw\"),t=this.handles.split(\",\"),this.handles={},i=0;t.length>i;i++)s=e.trim(t[i]),a=\"ui-resizable-\"+s,n=e(\"<div class='ui-resizable-handle \"+a+\"'></div>\"),n.css({zIndex:r.zIndex}),\"se\"===s&&n.addClass(\"ui-icon ui-icon-gripsmall-diagonal-se\"),this.handles[s]=\".ui-resizable-\"+s,this.element.append(n);this._renderAxis=function(t){var i,s,n,a;t=t||this.element;for(i in this.handles)this.handles[i].constructor===String&&(this.handles[i]=e(this.handles[i],this.element).show()),this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)&&(s=e(this.handles[i],this.element),a=/sw|ne|nw|se|n|s/.test(i)?s.outerHeight():s.outerWidth(),n=[\"padding\",/ne|nw|n/.test(i)?\"Top\":/se|sw|s/.test(i)?\"Bottom\":/^e$/.test(i)?\"Right\":\"Left\"].join(\"\"),t.css(n,a),this._proportionallyResize()),e(this.handles[i]).length},this._renderAxis(this.element),this._handles=e(\".ui-resizable-handle\",this.element).disableSelection(),this._handles.mouseover(function(){o.resizing||(this.className&&(n=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)),o.axis=n&&n[1]?n[1]:\"se\")}),r.autoHide&&(this._handles.hide(),e(this.element).addClass(\"ui-resizable-autohide\").mouseenter(function(){r.disabled||(e(this).removeClass(\"ui-resizable-autohide\"),o._handles.show())}).mouseleave(function(){r.disabled||o.resizing||(e(this).addClass(\"ui-resizable-autohide\"),o._handles.hide())})),this._mouseInit()},_destroy:function(){this._mouseDestroy();var t,i=function(t){e(t).removeClass(\"ui-resizable ui-resizable-disabled ui-resizable-resizing\").removeData(\"resizable\").removeData(\"ui-resizable\").unbind(\".resizable\").find(\".ui-resizable-handle\").remove()};return this.elementIsWrapper&&(i(this.element),t=this.element,this.originalElement.css({position:t.css(\"position\"),width:t.outerWidth(),height:t.outerHeight(),top:t.css(\"top\"),left:t.css(\"left\")}).insertAfter(t),t.remove()),this.originalElement.css(\"resize\",this.originalResizeStyle),i(this.originalElement),this},_mouseCapture:function(t){var i,s,n=!1;for(i in this.handles)s=e(this.handles[i])[0],(s===t.target||e.contains(s,t.target))&&(n=!0);return!this.options.disabled&&n},_mouseStart:function(i){var s,n,a,o=this.options,r=this.element.position(),h=this.element;return this.resizing=!0,/absolute/.test(h.css(\"position\"))?h.css({position:\"absolute\",top:h.css(\"top\"),left:h.css(\"left\")}):h.is(\".ui-draggable\")&&h.css({position:\"absolute\",top:r.top,left:r.left}),this._renderProxy(),s=t(this.helper.css(\"left\")),n=t(this.helper.css(\"top\")),o.containment&&(s+=e(o.containment).scrollLeft()||0,n+=e(o.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:s,top:n},this.size=this._helper?{width:h.outerWidth(),height:h.outerHeight()}:{width:h.width(),height:h.height()},this.originalSize=this._helper?{width:h.outerWidth(),height:h.outerHeight()}:{width:h.width(),height:h.height()},this.originalPosition={left:s,top:n},this.sizeDiff={width:h.outerWidth()-h.width(),height:h.outerHeight()-h.height()},this.originalMousePosition={left:i.pageX,top:i.pageY},this.aspectRatio=\"number\"==typeof o.aspectRatio?o.aspectRatio:this.originalSize.width/this.originalSize.height||1,a=e(\".ui-resizable-\"+this.axis).css(\"cursor\"),e(\"body\").css(\"cursor\",\"auto\"===a?this.axis+\"-resize\":a),h.addClass(\"ui-resizable-resizing\"),this._propagate(\"start\",i),!0},_mouseDrag:function(t){var i,s=this.helper,n={},a=this.originalMousePosition,o=this.axis,r=this.position.top,h=this.position.left,l=this.size.width,u=this.size.height,c=t.pageX-a.left||0,d=t.pageY-a.top||0,p=this._change[o];return p?(i=p.apply(this,[t,c,d]),this._updateVirtualBoundaries(t.shiftKey),(this._aspectRatio||t.shiftKey)&&(i=this._updateRatio(i,t)),i=this._respectSize(i,t),this._updateCache(i),this._propagate(\"resize\",t),this.position.top!==r&&(n.top=this.position.top+\"px\"),this.position.left!==h&&(n.left=this.position.left+\"px\"),this.size.width!==l&&(n.width=this.size.width+\"px\"),this.size.height!==u&&(n.height=this.size.height+\"px\"),s.css(n),!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),e.isEmptyObject(n)||this._trigger(\"resize\",t,this.ui()),!1):!1},_mouseStop:function(t){this.resizing=!1;var i,s,n,a,o,r,h,l=this.options,u=this;return this._helper&&(i=this._proportionallyResizeElements,s=i.length&&/textarea/i.test(i[0].nodeName),n=s&&e.ui.hasScroll(i[0],\"left\")?0:u.sizeDiff.height,a=s?0:u.sizeDiff.width,o={width:u.helper.width()-a,height:u.helper.height()-n},r=parseInt(u.element.css(\"left\"),10)+(u.position.left-u.originalPosition.left)||null,h=parseInt(u.element.css(\"top\"),10)+(u.position.top-u.originalPosition.top)||null,l.animate||this.element.css(e.extend(o,{top:h,left:r})),u.helper.height(u.size.height),u.helper.width(u.size.width),this._helper&&!l.animate&&this._proportionallyResize()),e(\"body\").css(\"cursor\",\"auto\"),this.element.removeClass(\"ui-resizable-resizing\"),this._propagate(\"stop\",t),this._helper&&this.helper.remove(),!1},_updateVirtualBoundaries:function(e){var t,s,n,a,o,r=this.options;o={minWidth:i(r.minWidth)?r.minWidth:0,maxWidth:i(r.maxWidth)?r.maxWidth:1/0,minHeight:i(r.minHeight)?r.minHeight:0,maxHeight:i(r.maxHeight)?r.maxHeight:1/0},(this._aspectRatio||e)&&(t=o.minHeight*this.aspectRatio,n=o.minWidth/this.aspectRatio,s=o.maxHeight*this.aspectRatio,a=o.maxWidth/this.aspectRatio,t>o.minWidth&&(o.minWidth=t),n>o.minHeight&&(o.minHeight=n),o.maxWidth>s&&(o.maxWidth=s),o.maxHeight>a&&(o.maxHeight=a)),this._vBoundaries=o},_updateCache:function(e){this.offset=this.helper.offset(),i(e.left)&&(this.position.left=e.left),i(e.top)&&(this.position.top=e.top),i(e.height)&&(this.size.height=e.height),i(e.width)&&(this.size.width=e.width)},_updateRatio:function(e){var t=this.position,s=this.size,n=this.axis;return i(e.height)?e.width=e.height*this.aspectRatio:i(e.width)&&(e.height=e.width/this.aspectRatio),\"sw\"===n&&(e.left=t.left+(s.width-e.width),e.top=null),\"nw\"===n&&(e.top=t.top+(s.height-e.height),e.left=t.left+(s.width-e.width)),e},_respectSize:function(e){var t=this._vBoundaries,s=this.axis,n=i(e.width)&&t.maxWidth&&t.maxWidth<e.width,a=i(e.height)&&t.maxHeight&&t.maxHeight<e.height,o=i(e.width)&&t.minWidth&&t.minWidth>e.width,r=i(e.height)&&t.minHeight&&t.minHeight>e.height,h=this.originalPosition.left+this.originalSize.width,l=this.position.top+this.size.height,u=/sw|nw|w/.test(s),c=/nw|ne|n/.test(s);return o&&(e.width=t.minWidth),r&&(e.height=t.minHeight),n&&(e.width=t.maxWidth),a&&(e.height=t.maxHeight),o&&u&&(e.left=h-t.minWidth),n&&u&&(e.left=h-t.maxWidth),r&&c&&(e.top=l-t.minHeight),a&&c&&(e.top=l-t.maxHeight),e.width||e.height||e.left||!e.top?e.width||e.height||e.top||!e.left||(e.left=null):e.top=null,e},_proportionallyResize:function(){if(this._proportionallyResizeElements.length){var e,t,i,s,n,a=this.helper||this.element;for(e=0;this._proportionallyResizeElements.length>e;e++){if(n=this._proportionallyResizeElements[e],!this.borderDif)for(this.borderDif=[],i=[n.css(\"borderTopWidth\"),n.css(\"borderRightWidth\"),n.css(\"borderBottomWidth\"),n.css(\"borderLeftWidth\")],s=[n.css(\"paddingTop\"),n.css(\"paddingRight\"),n.css(\"paddingBottom\"),n.css(\"paddingLeft\")],t=0;i.length>t;t++)this.borderDif[t]=(parseInt(i[t],10)||0)+(parseInt(s[t],10)||0);n.css({height:a.height()-this.borderDif[0]-this.borderDif[2]||0,width:a.width()-this.borderDif[1]-this.borderDif[3]||0})}}},_renderProxy:function(){var t=this.element,i=this.options;this.elementOffset=t.offset(),this._helper?(this.helper=this.helper||e(\"<div style='overflow:hidden;'></div>\"),this.helper.addClass(this._helper).css({width:this.element.outerWidth()-1,height:this.element.outerHeight()-1,position:\"absolute\",left:this.elementOffset.left+\"px\",top:this.elementOffset.top+\"px\",zIndex:++i.zIndex}),this.helper.appendTo(\"body\").disableSelection()):this.helper=this.element},_change:{e:function(e,t){return{width:this.originalSize.width+t}},w:function(e,t){var i=this.originalSize,s=this.originalPosition;return{left:s.left+t,width:i.width-t}},n:function(e,t,i){var s=this.originalSize,n=this.originalPosition;return{top:n.top+i,height:s.height-i}},s:function(e,t,i){return{height:this.originalSize.height+i}},se:function(t,i,s){return e.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[t,i,s]))},sw:function(t,i,s){return e.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[t,i,s]))},ne:function(t,i,s){return e.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[t,i,s]))},nw:function(t,i,s){return e.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[t,i,s]))}},_propagate:function(t,i){e.ui.plugin.call(this,t,[i,this.ui()]),\"resize\"!==t&&this._trigger(t,i,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}),e.ui.plugin.add(\"resizable\",\"animate\",{stop:function(t){var i=e(this).data(\"ui-resizable\"),s=i.options,n=i._proportionallyResizeElements,a=n.length&&/textarea/i.test(n[0].nodeName),o=a&&e.ui.hasScroll(n[0],\"left\")?0:i.sizeDiff.height,r=a?0:i.sizeDiff.width,h={width:i.size.width-r,height:i.size.height-o},l=parseInt(i.element.css(\"left\"),10)+(i.position.left-i.originalPosition.left)||null,u=parseInt(i.element.css(\"top\"),10)+(i.position.top-i.originalPosition.top)||null;i.element.animate(e.extend(h,u&&l?{top:u,left:l}:{}),{duration:s.animateDuration,easing:s.animateEasing,step:function(){var s={width:parseInt(i.element.css(\"width\"),10),height:parseInt(i.element.css(\"height\"),10),top:parseInt(i.element.css(\"top\"),10),left:parseInt(i.element.css(\"left\"),10)};n&&n.length&&e(n[0]).css({width:s.width,height:s.height}),i._updateCache(s),i._propagate(\"resize\",t)}})}}),e.ui.plugin.add(\"resizable\",\"containment\",{start:function(){var i,s,n,a,o,r,h,l=e(this).data(\"ui-resizable\"),u=l.options,c=l.element,d=u.containment,p=d instanceof e?d.get(0):/parent/.test(d)?c.parent().get(0):d;p&&(l.containerElement=e(p),/document/.test(d)||d===document?(l.containerOffset={left:0,top:0},l.containerPosition={left:0,top:0},l.parentData={element:e(document),left:0,top:0,width:e(document).width(),height:e(document).height()||document.body.parentNode.scrollHeight}):(i=e(p),s=[],e([\"Top\",\"Right\",\"Left\",\"Bottom\"]).each(function(e,n){s[e]=t(i.css(\"padding\"+n))}),l.containerOffset=i.offset(),l.containerPosition=i.position(),l.containerSize={height:i.innerHeight()-s[3],width:i.innerWidth()-s[1]},n=l.containerOffset,a=l.containerSize.height,o=l.containerSize.width,r=e.ui.hasScroll(p,\"left\")?p.scrollWidth:o,h=e.ui.hasScroll(p)?p.scrollHeight:a,l.parentData={element:p,left:n.left,top:n.top,width:r,height:h}))},resize:function(t){var i,s,n,a,o=e(this).data(\"ui-resizable\"),r=o.options,h=o.containerOffset,l=o.position,u=o._aspectRatio||t.shiftKey,c={top:0,left:0},d=o.containerElement;d[0]!==document&&/static/.test(d.css(\"position\"))&&(c=h),l.left<(o._helper?h.left:0)&&(o.size.width=o.size.width+(o._helper?o.position.left-h.left:o.position.left-c.left),u&&(o.size.height=o.size.width/o.aspectRatio),o.position.left=r.helper?h.left:0),l.top<(o._helper?h.top:0)&&(o.size.height=o.size.height+(o._helper?o.position.top-h.top:o.position.top),u&&(o.size.width=o.size.height*o.aspectRatio),o.position.top=o._helper?h.top:0),o.offset.left=o.parentData.left+o.position.left,o.offset.top=o.parentData.top+o.position.top,i=Math.abs((o._helper?o.offset.left-c.left:o.offset.left-c.left)+o.sizeDiff.width),s=Math.abs((o._helper?o.offset.top-c.top:o.offset.top-h.top)+o.sizeDiff.height),n=o.containerElement.get(0)===o.element.parent().get(0),a=/relative|absolute/.test(o.containerElement.css(\"position\")),n&&a&&(i-=o.parentData.left),i+o.size.width>=o.parentData.width&&(o.size.width=o.parentData.width-i,u&&(o.size.height=o.size.width/o.aspectRatio)),s+o.size.height>=o.parentData.height&&(o.size.height=o.parentData.height-s,u&&(o.size.width=o.size.height*o.aspectRatio))},stop:function(){var t=e(this).data(\"ui-resizable\"),i=t.options,s=t.containerOffset,n=t.containerPosition,a=t.containerElement,o=e(t.helper),r=o.offset(),h=o.outerWidth()-t.sizeDiff.width,l=o.outerHeight()-t.sizeDiff.height;t._helper&&!i.animate&&/relative/.test(a.css(\"position\"))&&e(this).css({left:r.left-n.left-s.left,width:h,height:l}),t._helper&&!i.animate&&/static/.test(a.css(\"position\"))&&e(this).css({left:r.left-n.left-s.left,width:h,height:l})}}),e.ui.plugin.add(\"resizable\",\"alsoResize\",{start:function(){var t=e(this).data(\"ui-resizable\"),i=t.options,s=function(t){e(t).each(function(){var t=e(this);t.data(\"ui-resizable-alsoresize\",{width:parseInt(t.width(),10),height:parseInt(t.height(),10),left:parseInt(t.css(\"left\"),10),top:parseInt(t.css(\"top\"),10)})})};\"object\"!=typeof i.alsoResize||i.alsoResize.parentNode?s(i.alsoResize):i.alsoResize.length?(i.alsoResize=i.alsoResize[0],s(i.alsoResize)):e.each(i.alsoResize,function(e){s(e)})},resize:function(t,i){var s=e(this).data(\"ui-resizable\"),n=s.options,a=s.originalSize,o=s.originalPosition,r={height:s.size.height-a.height||0,width:s.size.width-a.width||0,top:s.position.top-o.top||0,left:s.position.left-o.left||0},h=function(t,s){e(t).each(function(){var t=e(this),n=e(this).data(\"ui-resizable-alsoresize\"),a={},o=s&&s.length?s:t.parents(i.originalElement[0]).length?[\"width\",\"height\"]:[\"width\",\"height\",\"top\",\"left\"];e.each(o,function(e,t){var i=(n[t]||0)+(r[t]||0);i&&i>=0&&(a[t]=i||null)}),t.css(a)})};\"object\"!=typeof n.alsoResize||n.alsoResize.nodeType?h(n.alsoResize):e.each(n.alsoResize,function(e,t){h(e,t)})},stop:function(){e(this).removeData(\"resizable-alsoresize\")}}),e.ui.plugin.add(\"resizable\",\"ghost\",{start:function(){var t=e(this).data(\"ui-resizable\"),i=t.options,s=t.size;t.ghost=t.originalElement.clone(),t.ghost.css({opacity:.25,display:\"block\",position:\"relative\",height:s.height,width:s.width,margin:0,left:0,top:0}).addClass(\"ui-resizable-ghost\").addClass(\"string\"==typeof i.ghost?i.ghost:\"\"),t.ghost.appendTo(t.helper)},resize:function(){var t=e(this).data(\"ui-resizable\");t.ghost&&t.ghost.css({position:\"relative\",height:t.size.height,width:t.size.width})},stop:function(){var t=e(this).data(\"ui-resizable\");t.ghost&&t.helper&&t.helper.get(0).removeChild(t.ghost.get(0))}}),e.ui.plugin.add(\"resizable\",\"grid\",{resize:function(){var t=e(this).data(\"ui-resizable\"),i=t.options,s=t.size,n=t.originalSize,a=t.originalPosition,o=t.axis,r=\"number\"==typeof i.grid?[i.grid,i.grid]:i.grid,h=r[0]||1,l=r[1]||1,u=Math.round((s.width-n.width)/h)*h,c=Math.round((s.height-n.height)/l)*l,d=n.width+u,p=n.height+c,f=i.maxWidth&&d>i.maxWidth,m=i.maxHeight&&p>i.maxHeight,g=i.minWidth&&i.minWidth>d,v=i.minHeight&&i.minHeight>p;i.grid=r,g&&(d+=h),v&&(p+=l),f&&(d-=h),m&&(p-=l),/^(se|s|e)$/.test(o)?(t.size.width=d,t.size.height=p):/^(ne)$/.test(o)?(t.size.width=d,t.size.height=p,t.position.top=a.top-c):/^(sw)$/.test(o)?(t.size.width=d,t.size.height=p,t.position.left=a.left-u):(t.size.width=d,t.size.height=p,t.position.top=a.top-c,t.position.left=a.left-u)}})})(jQuery);"
  },
  {
    "path": "talks/static/slides.js",
    "content": "// Copyright 2012 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\nvar PERMANENT_URL_PREFIX = '/static/';\n\nvar SLIDE_CLASSES = ['far-past', 'past', 'current', 'next', 'far-next'];\n\nvar PM_TOUCH_SENSITIVITY = 15;\n\nvar curSlide;\n\n/* ---------------------------------------------------------------------- */\n/* classList polyfill by Eli Grey\n * (http://purl.eligrey.com/github/classList.js/blob/master/classList.js) */\n\nif (typeof document !== \"undefined\" && !(\"classList\" in document.createElement(\"a\"))) {\n\n(function (view) {\n\nvar\n    classListProp = \"classList\"\n  , protoProp = \"prototype\"\n  , elemCtrProto = (view.HTMLElement || view.Element)[protoProp]\n  , objCtr = Object\n    strTrim = String[protoProp].trim || function () {\n    return this.replace(/^\\s+|\\s+$/g, \"\");\n  }\n  , arrIndexOf = Array[protoProp].indexOf || function (item) {\n    for (var i = 0, len = this.length; i < len; i++) {\n      if (i in this && this[i] === item) {\n        return i;\n      }\n    }\n    return -1;\n  }\n  // Vendors: please allow content code to instantiate DOMExceptions\n  , DOMEx = function (type, message) {\n    this.name = type;\n    this.code = DOMException[type];\n    this.message = message;\n  }\n  , checkTokenAndGetIndex = function (classList, token) {\n    if (token === \"\") {\n      throw new DOMEx(\n          \"SYNTAX_ERR\"\n        , \"An invalid or illegal string was specified\"\n      );\n    }\n    if (/\\s/.test(token)) {\n      throw new DOMEx(\n          \"INVALID_CHARACTER_ERR\"\n        , \"String contains an invalid character\"\n      );\n    }\n    return arrIndexOf.call(classList, token);\n  }\n  , ClassList = function (elem) {\n    var\n        trimmedClasses = strTrim.call(elem.className)\n      , classes = trimmedClasses ? trimmedClasses.split(/\\s+/) : []\n    ;\n    for (var i = 0, len = classes.length; i < len; i++) {\n      this.push(classes[i]);\n    }\n    this._updateClassName = function () {\n      elem.className = this.toString();\n    };\n  }\n  , classListProto = ClassList[protoProp] = []\n  , classListGetter = function () {\n    return new ClassList(this);\n  }\n;\n// Most DOMException implementations don't allow calling DOMException's toString()\n// on non-DOMExceptions. Error's toString() is sufficient here.\nDOMEx[protoProp] = Error[protoProp];\nclassListProto.item = function (i) {\n  return this[i] || null;\n};\nclassListProto.contains = function (token) {\n  token += \"\";\n  return checkTokenAndGetIndex(this, token) !== -1;\n};\nclassListProto.add = function (token) {\n  token += \"\";\n  if (checkTokenAndGetIndex(this, token) === -1) {\n    this.push(token);\n    this._updateClassName();\n  }\n};\nclassListProto.remove = function (token) {\n  token += \"\";\n  var index = checkTokenAndGetIndex(this, token);\n  if (index !== -1) {\n    this.splice(index, 1);\n    this._updateClassName();\n  }\n};\nclassListProto.toggle = function (token) {\n  token += \"\";\n  if (checkTokenAndGetIndex(this, token) === -1) {\n    this.add(token);\n  } else {\n    this.remove(token);\n  }\n};\nclassListProto.toString = function () {\n  return this.join(\" \");\n};\n\nif (objCtr.defineProperty) {\n  var classListPropDesc = {\n      get: classListGetter\n    , enumerable: true\n    , configurable: true\n  };\n  try {\n    objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);\n  } catch (ex) { // IE 8 doesn't support enumerable:true\n    if (ex.number === -0x7FF5EC54) {\n      classListPropDesc.enumerable = false;\n      objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);\n    }\n  }\n} else if (objCtr[protoProp].__defineGetter__) {\n  elemCtrProto.__defineGetter__(classListProp, classListGetter);\n}\n\n}(self));\n\n}\n/* ---------------------------------------------------------------------- */\n\n/* Slide movement */\n\nfunction hideHelpText() {\n  $('#help').hide();\n};\n\nfunction getSlideEl(no) {\n  if ((no < 0) || (no >= slideEls.length)) {\n    return null;\n  } else {\n    return slideEls[no];\n  }\n};\n\nfunction updateSlideClass(slideNo, className) {\n  var el = getSlideEl(slideNo);\n\n  if (!el) {\n    return;\n  }\n\n  if (className) {\n    el.classList.add(className);\n  }\n\n  for (var i in SLIDE_CLASSES) {\n    if (className != SLIDE_CLASSES[i]) {\n      el.classList.remove(SLIDE_CLASSES[i]);\n    }\n  }\n};\n\nfunction updateSlides() {\n  if (window.trackPageview) window.trackPageview();\n\n  for (var i = 0; i < slideEls.length; i++) {\n    switch (i) {\n      case curSlide - 2:\n        updateSlideClass(i, 'far-past');\n        break;\n      case curSlide - 1:\n        updateSlideClass(i, 'past');\n        break;\n      case curSlide:\n        updateSlideClass(i, 'current');\n        break;\n      case curSlide + 1:\n        updateSlideClass(i, 'next');\n        break;\n      case curSlide + 2:\n        updateSlideClass(i, 'far-next');\n        break;\n      default:\n        updateSlideClass(i);\n        break;\n    }\n  }\n\n  triggerLeaveEvent(curSlide - 1);\n  triggerEnterEvent(curSlide);\n\n  window.setTimeout(function() {\n    // Hide after the slide\n    disableSlideFrames(curSlide - 2);\n  }, 301);\n\n  enableSlideFrames(curSlide - 1);\n  enableSlideFrames(curSlide + 2);\n\n  updateHash();\n};\n\nfunction prevSlide() {\n  hideHelpText();\n  if (curSlide > 0) {\n    curSlide--;\n\n    updateSlides();\n  }\n};\n\nfunction nextSlide() {\n  hideHelpText();\n  if (curSlide < slideEls.length - 1) {\n    curSlide++;\n\n    updateSlides();\n  }\n};\n\n/* Slide events */\n\nfunction triggerEnterEvent(no) {\n  var el = getSlideEl(no);\n  if (!el) {\n    return;\n  }\n\n  var onEnter = el.getAttribute('onslideenter');\n  if (onEnter) {\n    new Function(onEnter).call(el);\n  }\n\n  var evt = document.createEvent('Event');\n  evt.initEvent('slideenter', true, true);\n  evt.slideNumber = no + 1; // Make it readable\n\n  el.dispatchEvent(evt);\n};\n\nfunction triggerLeaveEvent(no) {\n  var el = getSlideEl(no);\n  if (!el) {\n    return;\n  }\n\n  var onLeave = el.getAttribute('onslideleave');\n  if (onLeave) {\n    new Function(onLeave).call(el);\n  }\n\n  var evt = document.createEvent('Event');\n  evt.initEvent('slideleave', true, true);\n  evt.slideNumber = no + 1; // Make it readable\n\n  el.dispatchEvent(evt);\n};\n\n/* Touch events */\n\nfunction handleTouchStart(event) {\n  if (event.touches.length == 1) {\n    touchDX = 0;\n    touchDY = 0;\n\n    touchStartX = event.touches[0].pageX;\n    touchStartY = event.touches[0].pageY;\n\n    document.body.addEventListener('touchmove', handleTouchMove, true);\n    document.body.addEventListener('touchend', handleTouchEnd, true);\n  }\n};\n\nfunction handleTouchMove(event) {\n  if (event.touches.length > 1) {\n    cancelTouch();\n  } else {\n    touchDX = event.touches[0].pageX - touchStartX;\n    touchDY = event.touches[0].pageY - touchStartY;\n    event.preventDefault();\n  }\n};\n\nfunction handleTouchEnd(event) {\n  var dx = Math.abs(touchDX);\n  var dy = Math.abs(touchDY);\n\n  if ((dx > PM_TOUCH_SENSITIVITY) && (dy < (dx * 2 / 3))) {\n    if (touchDX > 0) {\n      prevSlide();\n    } else {\n      nextSlide();\n    }\n  }\n\n  cancelTouch();\n};\n\nfunction cancelTouch() {\n  document.body.removeEventListener('touchmove', handleTouchMove, true);\n  document.body.removeEventListener('touchend', handleTouchEnd, true);\n};\n\n/* Preloading frames */\n\nfunction disableSlideFrames(no) {\n  var el = getSlideEl(no);\n  if (!el) {\n    return;\n  }\n\n  var frames = el.getElementsByTagName('iframe');\n  for (var i = 0, frame; frame = frames[i]; i++) {\n    disableFrame(frame);\n  }\n};\n\nfunction enableSlideFrames(no) {\n  var el = getSlideEl(no);\n  if (!el) {\n    return;\n  }\n\n  var frames = el.getElementsByTagName('iframe');\n  for (var i = 0, frame; frame = frames[i]; i++) {\n    enableFrame(frame);\n  }\n};\n\nfunction disableFrame(frame) {\n  frame.src = 'about:blank';\n};\n\nfunction enableFrame(frame) {\n  var src = frame._src;\n\n  if (frame.src != src && src != 'about:blank') {\n    frame.src = src;\n  }\n};\n\nfunction setupFrames() {\n  var frames = document.querySelectorAll('iframe');\n  for (var i = 0, frame; frame = frames[i]; i++) {\n    frame._src = frame.src;\n    disableFrame(frame);\n  }\n\n  enableSlideFrames(curSlide);\n  enableSlideFrames(curSlide + 1);\n  enableSlideFrames(curSlide + 2);\n};\n\nfunction setupInteraction() {\n  /* Clicking and tapping */\n\n  var el = document.createElement('div');\n  el.className = 'slide-area';\n  el.id = 'prev-slide-area';\n  el.addEventListener('click', prevSlide, false);\n  document.querySelector('section.slides').appendChild(el);\n\n  var el = document.createElement('div');\n  el.className = 'slide-area';\n  el.id = 'next-slide-area';\n  el.addEventListener('click', nextSlide, false);\n  document.querySelector('section.slides').appendChild(el);\n\n  /* Swiping */\n\n  document.body.addEventListener('touchstart', handleTouchStart, false);\n}\n\n/* Hash functions */\n\nfunction getCurSlideFromHash() {\n  var slideNo = parseInt(location.hash.substr(1));\n\n  if (slideNo) {\n    curSlide = slideNo - 1;\n  } else {\n    curSlide = 0;\n  }\n};\n\nfunction updateHash() {\n  location.replace('#' + (curSlide + 1));\n};\n\n/* Event listeners */\n\nfunction handleBodyKeyDown(event) {\n  // If we're in a code element, only handle pgup/down.\n  var inCode = event.target.classList.contains(\"code\");\n\n  switch (event.keyCode) {\n    case 72: // 'H' hides the help text\n    case 27: // escape key\n      if (!inCode) hideHelpText();\n      break;\n\n    case 39: // right arrow\n    case 13: // Enter\n    case 32: // space\n      if (inCode) break;\n    case 34: // PgDn\n      nextSlide();\n      event.preventDefault();\n      break;\n\n    case 37: // left arrow\n    case 8: // Backspace\n      if (inCode) break;\n    case 33: // PgUp\n      prevSlide();\n      event.preventDefault();\n      break;\n\n    case 40: // down arrow\n      if (inCode) break;\n      nextSlide();\n      event.preventDefault();\n      break;\n\n    case 38: // up arrow\n      if (inCode) break;\n      prevSlide();\n      event.preventDefault();\n      break;\n  }\n};\n\nfunction addEventListeners() {\n  document.addEventListener('keydown', handleBodyKeyDown, false);\n};\n\n/* Initialization */\n\nfunction addFontStyle() {\n  var el = document.createElement('link');\n  el.rel = 'stylesheet';\n  el.type = 'text/css';\n  el.href = '//fonts.googleapis.com/css?family=' +\n            'Open+Sans:regular,semibold,italic,italicsemibold|Droid+Sans+Mono';\n\n  document.body.appendChild(el);\n};\n\nfunction addGeneralStyle() {\n  var el = document.createElement('link');\n  el.rel = 'stylesheet';\n  el.type = 'text/css';\n  el.href = PERMANENT_URL_PREFIX + 'styles.css';\n  document.body.appendChild(el);\n\n  var el = document.createElement('meta');\n  el.name = 'viewport';\n  el.content = 'width=1100,height=750';\n  document.querySelector('head').appendChild(el);\n\n  var el = document.createElement('meta');\n  el.name = 'apple-mobile-web-app-capable';\n  el.content = 'yes';\n  document.querySelector('head').appendChild(el);\n};\n\nfunction showHelpText() {\n};\n\nfunction handleDomLoaded() {\n  slideEls = document.querySelectorAll('section.slides > article');\n\n  setupFrames();\n\n  addFontStyle();\n  addGeneralStyle();\n  addEventListeners();\n\n  updateSlides();\n\n  setupInteraction();\n\n  if (window.location.hostname == \"localhost\" || window.location.hostname == \"127.0.0.1\" || window.location.hostname == \"::1\") {\n    hideHelpText();\n  }\n\n  document.body.classList.add('loaded');\n};\n\nfunction initialize() {\n  getCurSlideFromHash();\n\n  if (window['_DEBUG']) {\n    PERMANENT_URL_PREFIX = '../';\n  }\n\n  if (window['_DCL']) {\n    handleDomLoaded();\n  } else {\n    document.addEventListener('DOMContentLoaded', handleDomLoaded, false);\n  }\n}\n\n// If ?debug exists then load the script relative instead of absolute\nif (!window['_DEBUG'] && document.location.href.indexOf('?debug') !== -1) {\n  document.addEventListener('DOMContentLoaded', function() {\n    // Avoid missing the DomContentLoaded event\n    window['_DCL'] = true\n  }, false);\n\n  window['_DEBUG'] = true;\n  var script = document.createElement('script');\n  script.type = 'text/javascript';\n  script.src = '../slides.js';\n  var s = document.getElementsByTagName('script')[0];\n  s.parentNode.insertBefore(script, s);\n\n  // Remove this script\n  s.parentNode.removeChild(s);\n} else {\n  initialize();\n}\n"
  },
  {
    "path": "talks/static/styles.css",
    "content": "@media screen {\n  /* Framework */\n  html {\n    height: 100%;\n  }\n\n  body {\n    margin: 0;\n    padding: 0;\n\n    display: block !important;\n\n    height: 100%;\n    min-height: 740px;\n\n    overflow-x: hidden;\n    overflow-y: auto;\n\n    background: rgb(215, 215, 215);\n    background: -o-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));\n    background: -moz-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));\n    background: -webkit-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));\n    background: -webkit-gradient(radial, 50% 50%, 0, 50% 50%, 500, from(rgb(240, 240, 240)), to(rgb(190, 190, 190)));\n\n    -webkit-font-smoothing: antialiased;\n  }\n\n  .slides {\n    width: 100%;\n    height: 100%;\n    left: 0;\n    top: 0;\n\n    position: absolute;\n\n    -webkit-transform: translate3d(0, 0, 0);\n  }\n\n  .slides > article {\n    display: block;\n\n    position: absolute;\n    overflow: hidden;\n\n    width: 900px;\n    height: 700px;\n\n    left: 50%;\n    top: 50%;\n\n    margin-left: -450px;\n    margin-top: -350px;\n\n    padding: 40px 60px;\n\n    box-sizing: border-box;\n    -o-box-sizing: border-box;\n    -moz-box-sizing: border-box;\n    -webkit-box-sizing: border-box;\n\n    border-radius: 10px;\n    -o-border-radius: 10px;\n    -moz-border-radius: 10px;\n    -webkit-border-radius: 10px;\n\n    background-color: white;\n    background-image: url(/static/img/background.png);\n    background-position: right;\n    background-size: cover;\n\n    border: 1px solid rgba(0, 0, 0, .3);\n\n    transition: transform .3s ease-out;\n    -o-transition: -o-transform .3s ease-out;\n    -moz-transition: -moz-transform .3s ease-out;\n    -webkit-transition: -webkit-transform .3s ease-out;\n  }\n  .slides.layout-widescreen > article {\n    margin-left: -550px;\n    width: 1100px;\n  }\n  .slides.layout-faux-widescreen > article {\n    margin-left: -550px;\n    width: 1100px;\n\n    padding: 40px 160px;\n  }\n\n  .slides.layout-widescreen > article:not(.nobackground):not(.biglogo),\n  .slides.layout-faux-widescreen > article:not(.nobackground):not(.biglogo) {\n    background-position-x: 0, 840px;\n  }\n\n  /* Clickable/tappable areas */\n\n  .slide-area {\n    z-index: 1000;\n\n    position: absolute;\n    left: 0;\n    top: 0;\n    width: 150px;\n    height: 700px;\n\n    left: 50%;\n    top: 50%;\n\n    cursor: pointer;\n    margin-top: -350px;\n\n    tap-highlight-color: transparent;\n    -o-tap-highlight-color: transparent;\n    -moz-tap-highlight-color: transparent;\n    -webkit-tap-highlight-color: transparent;\n  }\n  #prev-slide-area {\n    margin-left: -550px;\n  }\n  #next-slide-area {\n    margin-left: 400px;\n  }\n  .slides.layout-widescreen #prev-slide-area,\n  .slides.layout-faux-widescreen #prev-slide-area {\n    margin-left: -650px;\n  }\n  .slides.layout-widescreen #next-slide-area,\n  .slides.layout-faux-widescreen #next-slide-area {\n    margin-left: 500px;\n  }\n\n  /* Slides */\n\n  .slides > article {\n    display: none;\n  }\n  .slides > article.far-past {\n    display: block;\n    transform: translate(-2040px);\n    -o-transform: translate(-2040px);\n    -moz-transform: translate(-2040px);\n    -webkit-transform: translate3d(-2040px, 0, 0);\n  }\n  .slides > article.past {\n    display: block;\n    transform: translate(-1020px);\n    -o-transform: translate(-1020px);\n    -moz-transform: translate(-1020px);\n    -webkit-transform: translate3d(-1020px, 0, 0);\n  }\n  .slides > article.current {\n    display: block;\n    transform: translate(0);\n    -o-transform: translate(0);\n    -moz-transform: translate(0);\n    -webkit-transform: translate3d(0, 0, 0);\n  }\n  .slides > article.next {\n    display: block;\n    transform: translate(1020px);\n    -o-transform: translate(1020px);\n    -moz-transform: translate(1020px);\n    -webkit-transform: translate3d(1020px, 0, 0);\n  }\n  .slides > article.far-next {\n    display: block;\n    transform: translate(2040px);\n    -o-transform: translate(2040px);\n    -moz-transform: translate(2040px);\n    -webkit-transform: translate3d(2040px, 0, 0);\n  }\n\n  .slides.layout-widescreen > article.far-past,\n  .slides.layout-faux-widescreen > article.far-past {\n    display: block;\n    transform: translate(-2260px);\n    -o-transform: translate(-2260px);\n    -moz-transform: translate(-2260px);\n    -webkit-transform: translate3d(-2260px, 0, 0);\n  }\n  .slides.layout-widescreen > article.past,\n  .slides.layout-faux-widescreen > article.past {\n    display: block;\n    transform: translate(-1130px);\n    -o-transform: translate(-1130px);\n    -moz-transform: translate(-1130px);\n    -webkit-transform: translate3d(-1130px, 0, 0);\n  }\n  .slides.layout-widescreen > article.current,\n  .slides.layout-faux-widescreen > article.current {\n    display: block;\n    transform: translate(0);\n    -o-transform: translate(0);\n    -moz-transform: translate(0);\n    -webkit-transform: translate3d(0, 0, 0);\n  }\n  .slides.layout-widescreen > article.next,\n  .slides.layout-faux-widescreen > article.next {\n    display: block;\n    transform: translate(1130px);\n    -o-transform: translate(1130px);\n    -moz-transform: translate(1130px);\n    -webkit-transform: translate3d(1130px, 0, 0);\n  }\n  .slides.layout-widescreen > article.far-next,\n  .slides.layout-faux-widescreen > article.far-next {\n    display: block;\n    transform: translate(2260px);\n    -o-transform: translate(2260px);\n    -moz-transform: translate(2260px);\n    -webkit-transform: translate3d(2260px, 0, 0);\n  }\n}\n\n@media print {\n  /* Set page layout */\n  @page {\n    size: A4 landscape;\n  }\n\n  body {\n    display: block !important;\n  }\n\n  .slides > article {\n    display: block;\n\n    position: relative;\n\n    page-break-inside: never;\n    page-break-after: always;\n\n    overflow: hidden;\n  }\n\n  h2 {\n    position: static !important;\n    margin-top: 400px !important;\n    margin-bottom: 100px !important;\n  }\n\n  div.code {\n    background: rgb(240, 240, 240);\n  }\n\n  /* Add explicit links */\n  a:link:after, a:visited:after {\n   content: \" (\" attr(href) \") \";\n   font-size: 50%;\n  }\n\n  #help {\n    display: none;\n    visibility: hidden;\n  }\n}\n\n/* Styles for slides */\n\n.slides > article {\n  font-family: 'Open Sans', Arial, sans-serif;\n\n  color: black;\n  text-shadow: 0 1px 1px rgba(0, 0, 0, .1);\n\n  font-size: 26px;\n  line-height: 36px;\n\n  letter-spacing: -1px;\n}\n\nb {\n  font-weight: 600;\n}\n\na {\n  color: rgb(0, 102, 204);\n  text-decoration: none;\n}\na:visited {\n  color: rgba(0, 102, 204, .75);\n}\na:hover {\n  color: black;\n}\n\np {\n  margin: 0;\n  padding: 0;\n\n  margin-top: 20px;\n}\np:first-child {\n  margin-top: 0;\n}\n\nh1 {\n  font-size: 60px;\n  line-height: 60px;\n\n  padding: 0;\n  margin: 0;\n  margin-top: 200px;\n  margin-bottom: 5px;\n  padding-right: 40px;\n\n  font-weight: 600;\n\n  letter-spacing: -3px;\n\n  color: rgb(51, 51, 51);\n}\n\nh2 {\n  font-size: 45px;\n  line-height: 45px;\n\n  position: absolute;\n  bottom: 150px;\n\n  padding: 0;\n  margin: 0;\n  padding-right: 40px;\n\n  font-weight: 600;\n\n  letter-spacing: -2px;\n\n  color: rgb(51, 51, 51);\n}\n\nh3 {\n  font-size: 30px;\n  line-height: 36px;\n\n  padding: 0;\n  margin: 0;\n  padding-right: 40px;\n\n  font-weight: 600;\n\n  letter-spacing: -1px;\n\n  color: rgb(51, 51, 51);\n}\n\nul {\n  margin: 0;\n  padding: 0;\n  margin-top: 20px;\n  margin-left: 1.5em;\n}\nli {\n  padding: 0;\n  margin: 0 0 .5em 0;\n}\n\ndiv.code {\n  padding: 5px 10px;\n  margin-top: 20px;\n  margin-bottom: 20px;\n  overflow: hidden;\n\n  background: rgb(240, 240, 240);\n  border: 1px solid rgb(224, 224, 224);\n}\npre {\n  margin: 0;\n  padding: 0;\n\n  font-family: 'Droid Sans Mono', 'Courier New', monospace;\n  font-size: 18px;\n  line-height: 24px;\n  letter-spacing: -1px;\n\n  color: black;\n}\n\ncode {\n  font-size: 95%;\n  font-family: 'Droid Sans Mono', 'Courier New', monospace;\n\n  color: black;\n}\n\narticle > .image,\narticle > .video {\n  text-align: center;\n  margin-top: 40px;\n}\n\narticle > .background {\n  position: absolute;\n  top: 0;\n  left: 0;\n  right: 0;\n  bottom: 0;\n  z-index: -1;\n}\n\narticle > .background > img {\n  max-height: 100%;\n  max-width: 100%;\n}\n\ntable {\n  width: 100%;\n  border-collapse: collapse;\n  margin-top: 40px;\n}\nth {\n  font-weight: 600;\n  text-align: left;\n}\ntd,\nth {\n  border: 1px solid rgb(224, 224, 224);\n  padding: 5px 10px;\n  vertical-align: top;\n}\n\np.link {\n  margin-left: 20px;\n}\n\n/* Code */\ndiv.code {\n  outline: 0px solid transparent;\n}\ndiv.playground {\n  position: relative;\n}\ndiv.output {\n  position: absolute;\n  left: 50%;\n  top: 50%;\n  right: 40px;\n  bottom: 40px;\n  background: #202020;\n  padding: 5px 10px;\n  z-index: 2;\n\n  border-radius: 10px;\n  -o-border-radius: 10px;\n  -moz-border-radius: 10px;\n  -webkit-border-radius: 10px;\n\n}\ndiv.output pre {\n  margin: 0;\n  padding: 0;\n  background: none;\n  border: none;\n  width: 100%;\n  height: 100%;\n  overflow: auto;\n}\ndiv.output .stdout, div.output pre {\n  color: #e6e6e6;\n}\ndiv.output .stderr, div.output .error {\n  color: rgb(255, 200, 200);\n}\ndiv.output .system, div.output .exit {\n  color: rgb(255, 230, 120)\n}\n.buttons {\n  position: relative;\n  float: right;\n  top: -60px;\n  right: 10px;\n}\ndiv.output .buttons {\n  position: absolute;\n  float: none;\n  top: auto;\n  right: 5px;\n  bottom: 5px;\n}\n\n/* Presenter details */\n.presenter {\n  margin-top: 20px;\n}\n.presenter p,\n.presenter .link {\n  margin: 0;\n  font-size: 28px;\n  line-height: 1.2em;\n}\n\n/* Output resize details */\n.ui-resizable-handle {\n  position: absolute;\n}\n.ui-resizable-n {\n  cursor: n-resize;\n  height: 7px;\n  width: 100%;\n  top: -5px;\n  left: 0;\n}\n.ui-resizable-w {\n  cursor: w-resize;\n  width: 7px;\n  left: -5px;\n  top: 0;\n  height: 100%;\n}\n.ui-resizable-nw {\n  cursor: nw-resize;\n  width: 9px;\n  height: 9px;\n  left: -5px;\n  top: -5px;\n}\niframe {\n  border: none;\n}\nfigcaption {\n  color: #666;\n  text-align: center;\n  font-size: 0.75em;\n}\n\n#help {\n  font-family: 'Open Sans', Arial, sans-serif;\n  text-align: center;\n  color: white;\n  background: #000;\n  opacity: 0.5;\n  position: fixed;\n  bottom: 25px;\n  left: 50px;\n  right: 50px;\n  padding: 20px;\n\n  border-radius: 10px;\n  -o-border-radius: 10px;\n  -moz-border-radius: 10px;\n  -webkit-border-radius: 10px;\n}\n\nheader > .icl-logo {\n  position: absolute;\n  left: 50px;\n  top: 50px;\n  background-image: url(/static/img/icl-logo.jpg);\n  background-position: top left;\n  background-size: auto 40px;\n  background-repeat: no-repeat;\n  height: 40px;\n  width: 160px;\n}\n\nheader > .mrg-logo {\n  position: absolute;\n  right: 50px;\n  top: 50px;\n  background-image: url(/static/img/mrg-logo.png);\n  background-position: top right;\n  background-size: auto 40px;\n  background-repeat: no-repeat;\n  height: 40px;\n  width: 40px;\n}\n\nfooter {\n  position: absolute;\n  bottom: 0;\n  left: 0;\n  font-size: medium;\n  width: 100%;\n  text-align: center;\n}\n\nfooter > .page-number {\n  position: absolute;\n  right: 10px;\n  bottom: 10px;\n  vertical-align: bottom;\n  height: 24px;\n}\n\nfooter > .mrg-logo {\n  position: absolute;\n  left: 10px;\n  bottom: 10px;\n  background-image: url(/static/img/mrg-logo.png);\n  background-position: top left;\n  background-repeat: no-repeat;\n  background-size: 24px 24px;\n  height: 24px;\n  min-width: 24px;\n}\n\nfooter > .mrg-logo:after {\n  height: 24px;\n  font-size: small;\n  vertical-align: top;\n  padding-left: 30px;\n  color: #333;\n  content: 'http://mrg.doc.ic.ac.uk';\n}\n\na[href='#colour:blue'] {\n  color: darkblue;\n  cursor: default;\n}\n\na[href='#colour:red'] {\n  color: darkred;\n  cursor: default;\n}\n\na[href='#colour:green'] {\n  color: green;\n  cursor: default;\n}\n\ndiv.title-img {\n  background-position: center;\n  background-repeat: no-repeat;\n  background-image: url(/conf-cc-2016/idea.png);\n  width: 100%;\n  height: 400px;\n}\n"
  },
  {
    "path": "talks/talk-golanguk-2016/calculi.html",
    "content": "<div style='padding: 2%'></div>\n<div>\n<div style='display: inline-block; width: 200px;'>\n    <img src='talk-golanguk-2016/hoare.jpg'/><br/>\n    <strong style='text-align: center'>Tony Hoare</strong>\n</div>\n<div style='display: inline-block; vertical-align: top'>\n    <ul><li>Quicksort, Hoare Logic, etc.</li>\n        <li><strong>CSP</strong> - Communicating Sequential Processes (1978)</li>\n        <li>Turing Award (1980)</li>\n    </ul>\n    <br/>\n    <br/>\n</div>\n</div>\n<div>\n<div style='display: inline-block; width: 200px;'>\n    <img src='talk-golanguk-2016/milner.jpg'/><br/>\n    <strong style='text-align: center'>Robin Milner</strong>\n</div>\n<div style='display: inline-block; vertical-align: top'>\n    <ul><li>ML programming language, LCF, etc.</li>\n        <li><strong>CCS</strong> - Calculus of Communicating Systems (1980)</li>\n        <li><strong>&pi;-calculus</strong> (1992)\n            <!--<ul><li><i>Channel passing</i>, e.g. <small><code>make(chan chan T)</code></small></li></ul>--></li>\n        <li>Turing Award (1991)</li>\n    </ul>\n</div>\n</div>\n"
  },
  {
    "path": "talks/talk-golanguk-2016/compat.html",
    "content": "<div class='image'>\n    <div style='display: inline-block'><img src='talk-golanguk-2016/compat.svg'/></div>\n    <div style='display: inline-block; width: 10%'></div>\n    <div style='display: inline-block'><img src='talk-golanguk-2016/incompat.svg'/></div>\n</div>\n"
  },
  {
    "path": "talks/talk-golanguk-2016/datatype.go",
    "content": "// +build OMIT\n\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\tx := 42\n\tfmt.Printf(\"Type of x is %T\", x)\n\tx = \"hello\" // incompatible!\n}\n"
  },
  {
    "path": "talks/talk-golanguk-2016/deadlock-notdetected.go",
    "content": "// +build OMIT\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc Sender(ch chan<- int) {\n\tch <- 42\n}\n\nfunc Receiver(ch <-chan int, done chan<- int) {\n\tdone <- <-ch\n}\n\nfunc main() {\n\tch := make(chan int)\n\tdone := make(chan int)\n\tgo Sender(ch)\n\tgo Receiver(ch, done)\n\tgo Receiver(ch, done) // Who is ch receiving from? // HLoops\n\t// Unreleated worker goroutine, keeps deadlock 'alive'\n\tgo func() { // HLwork\n\t\tfor i := 0; i < 2; i++ { // HLwork\n\t\t\tfmt.Println(\"Working #\", i)        // HLwork\n\t\t\ttime.Sleep(500 * time.Millisecond) // HLwork\n\t\t} // HLwork\n\t\tfmt.Println(\"-------- Worker finished --------\") // HLwork\n\t}() // HLwork\n\n\tfmt.Println(\"Done 1:\", <-done)\n\tfmt.Println(\"Done 2:\", <-done) // HLoops\n}\n"
  },
  {
    "path": "talks/talk-golanguk-2016/deadlock.go",
    "content": "// +build OMIT\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\n// Anonymous goroutine: Some long running work (e.g. http service)\nfunc Work() {\n\tfor i := 0; ; i++ {\n\t\tfmt.Println(\"Working #\", i)\n\t\ttime.Sleep(1 * time.Second)\n\t}\n}\n\n// START OMIT\nfunc Sender(ch chan<- int) {\n\tch <- 42\n}\nfunc Receiver(ch <-chan int, done chan<- int) {\n\tdone <- <-ch\n}\n\nfunc main() {\n\tch := make(chan int)\n\tdone := make(chan int)\n\tgo Sender(ch)\n\tgo Receiver(ch, done)\n\tgo Receiver(ch, done) // Who is ch receiving from? // HLoops\n\n\tfmt.Println(\"Done 1:\", <-done)\n\tfmt.Println(\"Done 2:\", <-done) // HLoops\n}\n\n// END OMIT\n"
  },
  {
    "path": "talks/talk-golanguk-2016/main-type.html",
    "content": "<div>\n<div class=\"code\" contenteditable=\"true\" spellcheck=\"false\" style='display: inline-block; vertical-align: middle'>\n<pre><span num=\"26\">func main() {</span>\n<span num=\"27\">    ch := make(chan int)</span>\n<span num=\"28\">    done := make(chan int)</span>\n<span num=\"29\"></span>\n<span num=\"30\">    go Sender(ch)</span>\n<span num=\"31\">    go Receiver(ch, done)</span>\n<span num=\"32\">    go Receiver(ch, done) // Who is ch receiving from?</span>\n<span num=\"33\">    go Work() // Just an infinite loop</span>\n<span num=\"34\"></span>\n<span num=\"35\">    fmt.Println(\"Done 1:\", &lt;-done)</span>\n<span num=\"36\">    fmt.Println(\"Done 2:\", &lt;-done)</span>\n<span num=\"37\">}</span>\n</pre>\n</div>\n<div style='display: inline-block; width: 10%'></div>\n<div style='display: inline-block; vertical-align: middle'>\n<div class=\"image\"><img src=\"talk-golanguk-2016/main-type.svg\"></div>\n<figcaption>Session types for main()</figcaption>\n</div >\n</div>\n"
  },
  {
    "path": "talks/talk-golanguk-2016/proc.txt",
    "content": "// +build OMIT\nfunc checkdead() {\n...\n\t// -1 for sysmon\n\trun := sched.mcount - sched.nmidle - sched.nmidlelocked - 1 // HL\n\tif run > 0 {\n\t\treturn\n\t}\n...\n\tgetg().m.throwing = -1 // do not dump full stacks\n\tthrow(\"all goroutines are asleep - deadlock!\")\n}\n"
  },
  {
    "path": "talks/talk-golanguk-2016/receiver-type.html",
    "content": "<div>\n<div class=\"code\" contenteditable=\"true\" spellcheck=\"false\" style='display: inline-block; width: 50%; vertical-align: middle'>\n<pre><span num=\"14\">func Receiver(ch &lt;-chan int, done chan&lt;- int) {</span>\n<span num=\"15\">    done &lt;- &lt;-ch</span>\n<span num=\"16\">}</span>\n</pre>\n</div>\n<div style='display: inline-block; width: 10%'></div>\n<div style='display: inline-block; vertical-align: middle'>\n<div class=\"image\"><img src=\"talk-golanguk-2016/receiver-type.svg\"></div>\n</div>\n</div>\n"
  },
  {
    "path": "talks/talk-golanguk-2016/sender-type.html",
    "content": "<div>\n<div class=\"code\" contenteditable=\"true\" spellcheck=\"false\" style='display: inline-block; width: 50%; vertical-align: middle'>\n<pre><span num=\"10\">func Sender(ch chan&lt;- int) {</span>\n<span num=\"11\">    ch &lt;- 42</span>\n<span num=\"12\">}</span>\n</pre>\n</div>\n<div style='display: inline-block; width: 10%'></div>\n<div style='display: inline-block; vertical-align: middle'>\n<div class=\"image\"><img src=\"talk-golanguk-2016/sender-type.svg\"></div>\n</div>\n</div>\n"
  },
  {
    "path": "talks/talk-golanguk-2016/video.html",
    "content": "<iframe width=\"560\" height=\"315\"\n    src=\"https://www.youtube.com/embed/VEGbCjOaMu8?t=1m23s\"\n    frameborder=\"0\" allowfullscreen></iframe>\n"
  },
  {
    "path": "talks/talk-golanguk-2016/work-type.html",
    "content": "<div>\n<div class=\"code\" contenteditable=\"true\" spellcheck=\"false\" style='display: inline-block; width: 50%; vertical-align: middle'>\n<pre><span num=>func() {          </span>\n<span num=\"26\">    for i := 0; i &lt; 4; i&#43;&#43; {</span>\n<span num=\"27\">        fmt.Println(&#34;Working #&#34;, i)       </span>\n<span num=\"28\">        time.Sleep(250 * time.Millisecond)</span>\n<span num=\"29\">    }</span>\n<span num=\"30\">}</span>\n</pre>\n</div>\n<div style='display: inline-block; width: 10%'></div>\n<div style='display: inline-block; vertical-align: middle'>\n<div class=\"image\"><img src=\"talk-golanguk-2016/work-type.svg\"></div>\n</div>\n</div>\n"
  },
  {
    "path": "talks/talk-golanguk-2016.slide",
    "content": "Static Deadlock Detection for Go\n\nNicholas Ng\n\nImperial College London\n@nicholascwng\nnickng@imperial.ac.uk\n\nhttp://mrg.doc.ic.ac.uk\n\n* Go and concurrency\n\n*Don't*communicate*by*sharing*memory,*share*memory*by*communicating.*\n\nAvoid locks for high-level concurrency\n\n- *Message-passing* over channels\n- Coordinate and synchronise goroutines\n\nInspired by Hoare's Communicating Sequential Processes (CSP)\n\nExpressive message-passing concurrency model\n\n* Concurrency\n\nConcurrency is complicated\n\nConcurrency bugs are hard to find and fix (write perfect code if you can!)\n\nOur research aims to help _avoid_ concurrency bugs as much as possible\n\n* Concurrency problems: deadlocks\n\n*fatal*error:*all*goroutines*are*asleep*-*deadlock!*\n\n.play talk-golanguk-2016/deadlock.go /START OMIT/,/END OMIT/ HLoops\n\n* What causes a deadlock in Go?\n\n- Sender sends message, Receiver not ready\n- Sender blocks goroutine and wait until Receiver ready\n- Blocked goroutine goes to sleep\n\nDeadlock\n\n- Goroutine blocks waiting for something impossible (e.g. wait for each other)\n- Whole program gets stuck\n\n.image talk-golanguk-2016/deadlock.svg\n.caption A classic deadlock\n\n* Avoiding deadlocks\n\nRule of thumb: *Make*sure*sender/receiver*are*matching*and*compatible*\n\n.image talk-golanguk-2016/nodeadlock.svg\n\n- e.g. Send vs. Receive\n- e.g. Range over channel vs. `close`\n\nSounds simple, so why is it difficult in practice?\n\n- Communication intertwined with non-communicating code (hint: model)\n- Program control flow is not a single path from top to bottom\n\n* Go runtime deadlock detector\n\n- Builtin detector in Go runtime\n- Count non-blocked goroutines, deadlock if count == 0\n- Panic when deadlock is encountered\n\n.code talk-golanguk-2016/proc.txt\n.link https://golang.org/src/runtime/proc.go#L3358\n\n* Go runtime deadlock detector\n\nIf we add a benign, long-running goroutine to the mix..\n\n.play talk-golanguk-2016/deadlock-notdetected.go /^func main/,/^}/ HLwork\n\n_local_deadlock_: some (but not all) goroutines are stuck\n\n* Building a better deadlock detector\n\nWhat if we can...\n\n- Detect deadlock in program without executing (avoid deadlock)\n- Find local deadlocks too\n- Go specific: deadlocks in communication (message passing)\n\n* Static deadlock detector\n\nTool developed based on our research\n\n.link https://github.com/nickng/dingo-hunter\n\n- Static (compile-time) detection of deadlock\n- Help _prevent_ deadlocks\n- Ongoing research\n\n.image talk-golanguk-2016/qrcode.png\n\nAnalysed common concurrency patterns & open source projects\n\n* Static deadlock detector\n\n- Analyse Go *source*code*\n- Infer model representing communication only\n- Check if model could get stuck (deadlock)\n- Safe model, safe code\n\n.image talk-golanguk-2016/workflow.svg\n.caption Under the hood: Workflow of our static deadlock detector\n\n* Modelling concurrency\n\nTo find deadlocks in communication\n\n- Accurate representation of communication\n- Established ideas\n\nWho better to ask than pioneers of concurrency?\n\n* Models of concurrency: Process calculi\n\nProcess calculi: canonical models for communication\n\n.html talk-golanguk-2016/calculi.html\n\n* Models of concurrency: Process calculi\n\nProcess calculi: canonical models for communication\n\n- Formal model of how processes (i.e. goroutines) interact\n- This is where Go's concurrency model come from\n- Also what our deadlock detection technique built on\n\n.image talk-golanguk-2016/pi.png\n.caption Essence of communication: asynchronous π-calculus and Go\n\n* Models of concurrency: Process calculi\n\nProcess calculi: canonical models for communication\n\n- Does not stop _bad_ interactions (e.g. deadlocks)\n- Succinct abstractions for communication/concurrency\n- All we need is a way to check if model is compatible\n\n* Types\n\n- Go, a typed programming language\n- Compile-time, static type checking\n- Is variable _compatible_ with value? (i.e. same type)\n\n.play talk-golanguk-2016/datatype.go /^func main/,/^}/\n.image talk-golanguk-2016/datatype.svg\n.caption Type checking: are the types compatible?\n\n* Types for communication (1/2)\n\n- *Session*Types* - Honda, Vasconcelos, Kubo (1998)\n- _Multiparty_ Session Types - Honda, Yoshida, Carbone (2008)\n- Originates from types for π-calculus\n- Is sender _compatible_ with receiver?\n.html talk-golanguk-2016/compat.html\n.caption Compatibility of communication\n\n* Types for communication (2/2)\n\n- Is sender _compatible_ with receiver?\n.html talk-golanguk-2016/compat.html\n.caption Compatibility of communication\n\n- Compatible in terms of *communication*structure*\n- Also take into account _loops_, _if-then-else_ and other control flow elements\n- So we can _type-check_ process calculi model similarly as ordinary types\n\n* How to Go from code to model?\n\n- Extract model (session types) from source code\n- Take into consideration program control flow\n- Go's amazing tooling ecosystem to the rescue!\n\n.link https://www.youtube.com/watch?v=oorX84tBMqo Program Analysis by Francesc Campoy (GolangUK 2015)\n\n* Extracting session types from code (1/2)\n\n*Static*analysis*: Look for `make(chan`T)`, `ch`<-`x`, `<-ch`, `select`, `close`\n\n- Analyse source code using `go/ssa` without executing (flow sensitive)\n- `go/ssa` makes it easier to understand program control flow\n\nmain\n.html talk-golanguk-2016/main-type.html\n\n* Extracting session types from code (2/2)\n\nSender\n.html talk-golanguk-2016/sender-type.html\n\nReceiver _x_2_\n.html talk-golanguk-2016/receiver-type.html\n\nWorker (Non-communicating)\n.html talk-golanguk-2016/work-type.html\n\n* Compose the model\n\n- One session type per goroutine\n- Collect all session types from each goroutine\n- Are they compatible?\n\n.image talk-golanguk-2016/all-local.svg\n.caption Session types obtained from all goroutines in program\n\nNext: compose and check compatibility\n\n* Global graph\n\n- Combine all session types: a bird's eye view (_global_graph_) of messages\n- e.g. find matching Send and Receive\n- Global graph must be well-formed (i.e. satisfy side conditions)\n\n.image talk-golanguk-2016/global-graph.svg\n.caption Incompatible: this is not a well formed global graph\n\nNote: the actual analysis is much more involved\n\n* Conclusion\n\n- Process calculi influenced design of concurrency in Go\n- Applied current research on process calculi deadlock detection to Go\n- Hope to inspire more concurrency research\n\n*References*\n\n📄 From Communicating Machines to Graphical Choreographies\n_J._Lange,_E._Tuosto,_N._Yoshida_, POPL 2015\n\n📄 Static Deadlock Detection for Go by Global Session Graph Synthesis\n_N._Ng,_N._Yoshida_, CC 2016\n\n.caption Thanks to my colleagues behind this work - Nobuko, Julien, Bernardo\n\n* Conclusion\n\nMore to come soon: more comprehensive theory and updated tool\n\n.link https://github.com/nickng/dingo-hunter\n\n- Unbuffered channels\n- Limited support for 'dynamic' patterns (e.g. spawn new goroutines while running)\n\n*Demo*\n\n.html talk-golanguk-2016/video.html\n\n"
  },
  {
    "path": "talks/talk-jan-2016.slide",
    "content": "Static Deadlock Detection for Go\n\nNicholas Ng / nicholascwng\nImperial College London\n\nhttp://mrg.doc.ic.ac.uk\n\nnickng@imperial.ac.uk\n\n@nicholascwng\n\n* Go Programming Language\n\n- Open source\n- Statically typed\n- Compiled, systems programming language\n- Built-in channel-based concurrency\n\n* Concurrency in Go\n\n- Communicating Sequential Process (CSP) style channel-based concurrency\n- Composition of sequential code\n.image talk-jan-2016/csp.png\n.caption CSP example (\"Communicating Sequential Processes\", Hoare 2004)\n\n* Concurrency in Go: Goroutines and Channels\n\n- Goroutines: lightweight threads\n- Channels: 'link', message-passing between goroutines\n.play basic/concurrency.go /^func deepThought/,/END OMIT$/ HLsendrecv\n\n* Deadlocks in Go\n\n- CSP are prone to these\n- Communication deadlocks: blocked communication (for 1 or more goroutines)\n\n    fatal error: all goroutines are asleep - deadlock!\n\n.play basic/concurrency.go /^func deepThought/,/END OMIT$/ HLdl\n\n* Go language runtime and tools\n\n- Go has a _runtime_ deadlock [[http://github.com/golang/go/blob/d2c81ad84776edfb4c790666f1d80554b4393d46/src/runtime/proc.go#L3243][detector]]\n- Go has a _runtime_ race detector\n- Guidelines/concurrency patterns exists to prevent deadlocks\n.code snippet/proc.txt\n\n* Yes, but...\n\n- *Runtime* detector (if not in execution path, no error)\n- Non-global deadlocks ignored by runtime detector\n\n.play deadlock/deadlock.go /^func main/,/}$/\n\n- Developer experience, best practices (think `malloc` & `free` in C and `valgrind`)\n\n* Static analysis of Go source code\n\n.image talk-jan-2016/workflow.png\n- Research: π-calculus (process calculi like CSP) and Session Types\n- Try to find _potential_ deadlocks\n*1.* Convert to SSA Intermediate Representation (golang.org/x/tools/go/ssa)\n*2.* Extract communication (send/recv/select/close) as local types, ignore computation\n*3.* Model as Communicating FSM (state machine)\n\n* SSA IR\n\nSSA = Single Static Assignment\nIn short: variables are assigned once (updates = new version of variable)\n\n    func main():\n    0:                                                                entry P:0 S:0\n        t0 = make chan int 0:int                                       chan int\n        t1 = make chan int 0:int                                       chan int\n        t2 = changetype chan<- int <- chan int (t0)                  chan<- int\n        go Send(t2)\n        t3 = changetype <-chan int <- chan int (t0)                  <-chan int\n        t4 = changetype chan<- int <- chan int (t1)                  chan<- int\n        go Recv(t3, t4)\n        t5 = changetype <-chan int <- chan int (t0)                  <-chan int\n        t6 = changetype chan<- int <- chan int (t1)                  chan<- int\n        go Recv(t5, t6)\n        go Work()\n        t7 = <-t1                                                           int\n        t8 = <-t1                                                           int\n        return\n\n* Static Analysis\n\n- Track channel when they are created make(chan T)\n- Any send/receive on channel recorded as events (similar to Go oracle ChannelPeer)\n- But instead we build a graph of (all possible) events - we call it local types\n.image talk-jan-2016/extracted.png\n\n* Communicating Finite State Machine\n\nAim: Global (Session) Graph Synthesis\n\n- i.e. all send ⇆ all recv\n- Satisfy relaxed (synchronous) Generalised Multiparty Compatibility conditions (Lange and Yoshida, POPL'15)\n\n.image talk-jan-2016/cfsm.png\n.caption Left: Communicating Finite State Machines; Right: Global graph - deadlocked vs. fixed (consistent)\n\n* // TODOs\n\nTons of stuff missing but works on most simple programs\n\n- e.g. `sync` package, (Particularly `sync.WaitGroup`, i.e. barrier synchronisation)\n- e.g. buffered channels\n\n.link https://github.com/nickng/dingo-hunter\n.image talk-jan-2016/dingo-hunter.jpg\n"
  },
  {
    "path": "talks/templates/action.tmpl",
    "content": "{/*\nThis is the action template.\nIt determines how the formatting actions are rendered.\n*/}\n\n{{define \"section\"}}\n  <h{{len .Number}} id=\"TOC_{{.FormattedNumber}}\">{{.FormattedNumber}} {{.Title}}</h{{len .Number}}>\n  {{range .Elem}}{{elem $.Template .}}{{end}}\n{{end}}\n\n{{define \"list\"}}\n  <ul>\n  {{range .Bullet}}\n    <li>{{style .}}</li>\n  {{end}}\n  </ul>\n{{end}}\n\n{{define \"text\"}}\n  {{if .Pre}}\n  <div class=\"code\"><pre>{{range .Lines}}{{.}}{{end}}</pre></div>\n  {{else}}\n  <p>\n    {{range $i, $l := .Lines}}{{if $i}}{{template \"newline\"}}\n    {{end}}{{style $l}}{{end}}\n  </p>\n  {{end}}\n{{end}}\n\n{{define \"code\"}}\n  <div class=\"code{{if playable .}} playground{{end}}\" contenteditable=\"true\" spellcheck=\"false\">{{.Text}}</div>\n{{end}}\n\n{{define \"image\"}}\n<div class=\"image\">\n  <img src=\"{{.URL}}\"{{with .Height}} height=\"{{.}}\"{{end}}{{with .Width}} width=\"{{.}}\"{{end}}>\n</div>\n{{end}}\n\n{{define \"video\"}}\n<div class=\"video\">\n  <video {{with .Height}} height=\"{{.}}\"{{end}}{{with .Width}} width=\"{{.}}\"{{end}} controls>\n    <source src=\"{{.URL}}\" type=\"{{.SourceType}}\">\n  </video>\n</div>\n{{end}}\n\n{{define \"background\"}}\n<div class=\"background\">\n  <img src=\"{{.URL}}\">\n</div>\n{{end}}\n\n{{define \"iframe\"}}\n<iframe src=\"{{.URL}}\"{{with .Height}} height=\"{{.}}\"{{end}}{{with .Width}} width=\"{{.}}\"{{end}}></iframe>\n{{end}}\n\n{{define \"link\"}}<p class=\"link\"><a href=\"{{.URL}}\" target=\"_blank\">{{style .Label}}</a></p>{{end}}\n\n{{define \"html\"}}{{.HTML}}{{end}}\n\n{{define \"caption\"}}<figcaption>{{style .Text}}</figcaption>{{end}}\n"
  },
  {
    "path": "talks/templates/article.tmpl",
    "content": "{/* This is the article template. It defines how articles are formatted. */}\n\n{{define \"root\"}}\n<!DOCTYPE html>\n<html>\n  <head>\n    <title>{{.Title}}</title>\n    <link type=\"text/css\" rel=\"stylesheet\" href=\"/static/article.css\">\n    <meta charset='utf-8'>\n  </head>\n\n  <body>\n    <div id=\"topbar\" class=\"wide\">\n      <div class=\"container\">\n        <div id=\"heading\">{{.Title}}\n          {{with .Subtitle}}{{.}}{{end}}\n        </div>\n      </div>\n    </div>\n    <div id=\"page\" class=\"wide\">\n      <div class=\"container\">\n        {{with .Sections}}\n          <div id=\"toc\">\n            {{template \"TOC\" .}}\n          </div>\n        {{end}}\n\n        {{range .Sections}}\n          {{elem $.Template .}}\n        {{end}}{{/* of Section block */}}\n\n        {{if .Authors}}\n          <h2>Authors</h2>\n          {{range .Authors}}\n            <div class=\"author\">\n              {{range .Elem}}{{elem $.Template .}}{{end}}\n            </div>\n          {{end}}\n        {{end}}\n      </div>\n    </div>\n\n    {{if .PlayEnabled}}\n    <script src='/play.js'></script>\n    {{end}}\n  </body>\n</html>\n{{end}}\n\n{{define \"TOC\"}}\n  <ul>\n  {{range .}}\n    <li><a href=\"#TOC_{{.FormattedNumber}}\">{{.Title}}</a></li>\n    {{with .Sections}}{{template \"TOC\" .}}{{end}}\n  {{end}}\n  </ul>\n{{end}}\n\n{{define \"newline\"}}\n{{/* No automatic line break. Paragraphs are free-form. */}}\n{{end}}\n"
  },
  {
    "path": "talks/templates/dir.tmpl",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n  <title>Talks - The Go Programming Language</title>\n  <link type=\"text/css\" rel=\"stylesheet\" href=\"/static/dir.css\">\n  <script src=\"/static/dir.js\"></script>\n  <script>\n    // Initialize Google Analytics tracking code on production site only.\n    if (window[\"location\"] && window[\"location\"][\"hostname\"] == \"talks.golang.org\") {\n      var _gaq = _gaq || [];\n      _gaq.push([\"_setAccount\", \"UA-11222381-6\"]);\n      _gaq.push([\"b._setAccount\", \"UA-49880327-6\"]);\n      window.trackPageview = function() {\n        _gaq.push([\"_trackPageview\", location.pathname+location.hash]);\n        _gaq.push([\"b._trackPageview\", location.pathname+location.hash]);\n      };\n      window.trackPageview();\n      window.trackEvent = function(category, action, opt_label, opt_value, opt_noninteraction) {\n        _gaq.push([\"_trackEvent\", category, action, opt_label, opt_value, opt_noninteraction]);\n        _gaq.push([\"b._trackEvent\", category, action, opt_label, opt_value, opt_noninteraction]);\n      };\n    }\n  </script>\n</head>\n<body>\n\n<div id=\"topbar\"><div class=\"container\">\n\n<form method=\"GET\" action=\"//golang.org/search\">\n<div id=\"menu\">\n<a href=\"http://golang.org/doc/\">Documents</a>\n<a href=\"http://golang.org/ref\">References</a>\n<a href=\"http://golang.org/pkg/\">Packages</a>\n<a href=\"http://golang.org/project/\">The Project</a>\n<a href=\"http://golang.org/help/\">Help</a>\n<input type=\"text\" id=\"search\" name=\"q\" class=\"inactive\" value=\"Search\">\n</div>\n<div id=\"heading\"><a href=\"/\">The Go Programming Language</a></div>\n</form>\n\n</div></div>\n\n<div id=\"page\">\n\n  <h1>Go talks</h1>\n\n  {{with .Path}}<h2>{{.}}</h2>{{end}}\n\n  {{with .Articles}}\n  <h4>Articles:</h4>\n  <dl>\n  {{range .}}\n  <dd><a href=\"/{{.Path}}\">{{.Name}}</a>: {{.Title}}</dd>\n  {{end}}\n  </dl>\n  {{end}}\n\n  {{with .Slides}}\n  <h4>Slide decks:</h4>\n  <dl>\n  {{range .}}\n  <dd><a href=\"/{{.Path}}\">{{.Name}}</a>: {{.Title}}</dd>\n  {{end}}\n  </dl>\n  {{end}}\n\n  {{with .Other}}\n  <h4>Files:</h4>\n  <dl>\n  {{range .}}\n  <dd><a href=\"/{{.Path}}\">{{.Name}}</a></dd>\n  {{end}}\n  </dl>\n  {{end}}\n\n  {{with .Dirs}}\n  <h4>Sub-directories:</h4>\n  <dl>\n  {{range .}}\n  <dd><a href=\"/{{.Path}}\">{{.Name}}</a></dd>\n  {{end}}\n  </dl>\n  {{end}}\n\n</div>\n\n<div id=\"footer\">\nExcept as <a href=\"https://developers.google.com/site-policies#restrictions\">noted</a>,\nthe content of this page is licensed under the\nCreative Commons Attribution 3.0 License,\nand code is licensed under a <a href=\"http://golang.org/LICENSE\">BSD license</a>.<br>\n<a href=\"http://golang.org/doc/tos.html\">Terms of Service</a> |\n<a href=\"http://www.google.com/intl/en/policies/privacy/\">Privacy Policy</a>\n</div>\n\n<script>\n  (function() {\n    // Load Google Analytics tracking code on production site only.\n    if (window[\"location\"] && window[\"location\"][\"hostname\"] == \"talks.golang.org\") {\n      var ga = document.createElement(\"script\"); ga.type = \"text/javascript\"; ga.async = true;\n      ga.src = (\"https:\" == document.location.protocol ? \"https://ssl\" : \"http://www\") + \".google-analytics.com/ga.js\";\n      var s = document.getElementsByTagName(\"script\")[0]; s.parentNode.insertBefore(ga, s);\n    }\n  })();\n</script>\n</body>\n</html>\n"
  },
  {
    "path": "talks/templates/slides.tmpl",
    "content": "{/* This is the slide template. It defines how presentations are formatted. */}\n\n{{define \"root\"}}\n<!DOCTYPE html>\n<html>\n  <head>\n    <title>{{.Title}}</title>\n    <meta charset='utf-8'>\n    <script src='/static/slides.js'></script>\n  </head>\n\n  <body style='display: none'>\n\n    <section class='slides layout-widescreen'>\n\n      <article>\n        <header>\n          <div class=\"icl-logo\"></div>\n          <div class=\"mrg-logo\"></div>\n        </header>\n        <h1>{{.Title}}</h1>\n        {{with .Subtitle}}<h3>{{.}}</h3>{{end}}\n        {{if not .Time.IsZero}}<h3>{{.Time.Format \"2 January 2006\"}}</h3>{{end}}\n        {{range .Authors}}\n          <div class=\"presenter\">\n            {{range .TextElem}}{{elem $.Template .}}{{end}}\n          </div>\n        {{end}}\n      </article>\n\n  {{range $i, $s := .Sections}}\n  <!-- start of slide {{$s.Number}} -->\n      <article>\n      {{if $s.Elem}}\n        <h3>{{$s.Title}}</h3>\n        {{range $s.Elem}}{{elem $.Template .}}{{end}}\n      {{else}}\n        <div class=\"title-img\"></div>\n        <h2>{{$s.Title}}</h2>\n      {{end}}\n      <footer>\n        <div class=\"mrg-logo\"></div>\n        <div class=\"page-number\">{{index $s.Number 0}}</div>\n      </footer>\n      </article>\n  <!-- end of slide {{$i}} -->\n  {{end}}{{/* of Slide block */}}\n\n      <article>\n        <h3>Thank you</h3>\n        {{range .Authors}}\n          <div class=\"presenter\">\n            {{range .Elem}}{{elem $.Template .}}{{end}}\n          </div>\n        {{end}}\n      </article>\n\n    </section>\n\n    <div id=\"help\">\n      Use the left and right arrow keys or click the left and right\n      edges of the page to navigate between slides.<br>\n      (Press 'H' or navigate to hide this message.)\n    </div>\n\n    {{if .PlayEnabled}}\n    <script src='/play.js'></script>\n    {{end}}\n  </body>\n</html>\n{{end}}\n\n{{define \"newline\"}}\n<br>\n{{end}}\n"
  },
  {
    "path": "templates/index.tmpl",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"UTF-8\">\n  <title>{{.Title}}</title>\n  <link rel=\"stylesheet\" href=\"http://yui.yahooapis.com/pure/0.6.0/pure-min.css\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n  <link href='https://fonts.googleapis.com/css?family=Fira+Mono|Roboto:100,400' rel='stylesheet'/>\n  <link href=\"/static/style.css\" rel=\"stylesheet\" />\n</head>\n<body>\n  <header></header>\n  <div class='full-width'>\n  <div class='gocode'>\n    <div class='code playground' contenteditable='true' spellcheck='false' id='go'></div>\n  </div>\n  </div>\n  <div class='controls'>\n    <button name='cfsm' id='cfsm'>Show CFSM</button>\n    <button name='migo' id='migo'>Show MiGo</button>\n    <button name='ssa' id='ssa'>Show SSA</button>\n    {{- if .Examples -}}\n    <select name='example' id='examples' class='left'>\n        {{ range .Examples }}<option value='{{ . }}'>{{ . }}</option>{{ end }}\n    </select>\n    <button name='load' id='example' class='right'>Load</button>\n    {{- end -}}\n    <span id='time'></span>\n  </div>\n  <div class='generated'>\n    <div class='code' id='out' spellcheck='false' contenteditable='false'>No output.</div>\n    <div class='buttons'>\n        <button name='gong' id='gong'>Run Gong</button>\n        <button name='synthesis' id='synthesis'>Run Synthesis</button> <input name='chan-cfsm' id='chan-cfsm' value='1' placeholder='Chan CFSMs'/>\n    </div>\n    <div id='gong-wrap'><div id='gong-output'></div><div class='buttons'><button id='gong-output-close'>Close</button></div></div>\n    <div id='synthesis-wrap'><div id='synthesis-output'></div>\n        <div id='synthesis-graphics'><div id='synthesis-machines'></div><div id='synthesis-global'></div></div>\n        <div class='buttons'><button id='synthesis-output-close'>Close</button></div>\n    </div>\n  </div>\n  <script src='/play.js'></script>\n  <script src='/static/script.js'></script>\n</body>\n</html>\n"
  },
  {
    "path": "webservice/cfsm.go",
    "content": "package webservice\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"net/http\"\n\n\t\"github.com/nickng/dingo-hunter/cfsmextract\"\n\t\"github.com/nickng/dingo-hunter/cfsmextract/sesstype\"\n\t\"github.com/nickng/dingo-hunter/ssabuilder\"\n)\n\nfunc cfsmHandler(w http.ResponseWriter, req *http.Request) {\n\tb, err := ioutil.ReadAll(req.Body)\n\tif err != nil {\n\t\tNewErrInternal(err, \"Cannot read input Go source code\").Report(w)\n\t}\n\treq.Body.Close()\n\tconf, err := ssabuilder.NewConfigFromString(string(b))\n\tif err != nil {\n\t\tNewErrInternal(err, \"Cannot initialise SSA\").Report(w)\n\t}\n\tssainfo, err := conf.Build()\n\tif err != nil {\n\t\tNewErrInternal(err, \"Cannot build SSA\").Report(w)\n\t}\n\textract := cfsmextract.New(ssainfo, \"extract\", \"/tmp\")\n\tgo extract.Run()\n\n\tselect {\n\tcase <-extract.Error:\n\t\tNewErrInternal(err, \"CFSM extraction failed\").Report(w)\n\tcase <-extract.Done:\n\t\tlog.Println(\"CFSMs: analysis completed in\", extract.Time)\n\t}\n\tcfsms := sesstype.NewCFSMs(extract.Session())\n\tbufCfsm := new(bytes.Buffer)\n\tcfsms.WriteTo(bufCfsm)\n\tdot := sesstype.NewGraphvizDot(extract.Session())\n\tbufDot := new(bytes.Buffer)\n\tdot.WriteTo(bufDot)\n\treply := struct {\n\t\tCFSM string `json:\"CFSM\"`\n\t\tDot  string `json:\"dot\"`\n\t\tTime string `json:\"time\"`\n\t}{\n\t\tCFSM: bufCfsm.String(),\n\t\tDot:  bufDot.String(),\n\t\tTime: extract.Time.String(),\n\t}\n\tjson.NewEncoder(w).Encode(&reply)\n}\n"
  },
  {
    "path": "webservice/errors.go",
    "content": "package webservice\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n)\n\ntype ErrInternal struct {\n\tcause error\n\tmsg   string\n}\n\nfunc NewErrInternal(cause error, message string) *ErrInternal {\n\treturn &ErrInternal{cause: cause, msg: message}\n}\n\nfunc (e *ErrInternal) Error() string {\n\treturn fmt.Sprintf(\"%s: %v\", e.msg, e.cause)\n}\n\n// Report sends internal server error to web client also logs to console.\nfunc (e *ErrInternal) Report(w http.ResponseWriter) {\n\thttp.Error(w, e.Error(), http.StatusInternalServerError)\n\tlog.Fatal(e)\n}\n"
  },
  {
    "path": "webservice/gong.go",
    "content": "package webservice\n\nimport (\n\t\"encoding/json\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strings\"\n\t\"time\"\n)\n\nfunc gongHandler(w http.ResponseWriter, req *http.Request) {\n\tlog.Println(\"Running Gong on snippet\")\n\tb, err := ioutil.ReadAll(req.Body)\n\tif err != nil {\n\t\tNewErrInternal(err, \"Cannot read input MiGo types\").Report(w)\n\t}\n\treq.Body.Close()\n\tfile, err := ioutil.TempFile(os.TempDir(), \"gong\")\n\tif err != nil {\n\t\tNewErrInternal(err, \"Cannot create temp file for MiGo input\").Report(w)\n\t}\n\tdefer os.Remove(file.Name())\n\n\tif _, err := file.Write(b); err != nil {\n\t\tNewErrInternal(err, \"Cannot write to temp file for MiGo input\").Report(w)\n\t}\n\tif err := file.Close(); err != nil {\n\t\tNewErrInternal(err, \"Cannot close temp file for MiGo input\").Report(w)\n\t}\n\tGong, err := exec.LookPath(\"Gong\")\n\tif err != nil {\n\t\tNewErrInternal(err, \"Cannot find Gong executable (Check $PATH?)\").Report(w)\n\t}\n\tstartTime := time.Now()\n\tout, err := exec.Command(Gong, file.Name()).CombinedOutput()\n\tif err != nil {\n\t\tlog.Printf(\"Gong execution failed: %v\\n\", err)\n\t}\n\texecTime := time.Now().Sub(startTime)\n\treplacer := strings.NewReplacer(\"\u001b[92m\", \"<span style='color: #87ff87; font-weight: bold'>\", \"\u001b[91m\", \"<span style='color: #ff005f; font-weight: bold'>\", \"\u001b[0m\", \"</span>\")\n\treply := struct {\n\t\tGong string `json:\"Gong\"`\n\t\tTime string `json:\"time\"`\n\t}{\n\t\tGong: replacer.Replace(string(out)),\n\t\tTime: execTime.String(),\n\t}\n\tlog.Println(\"Gong completed in\", execTime.String())\n\tjson.NewEncoder(w).Encode(&reply)\n}\n"
  },
  {
    "path": "webservice/handlers.go",
    "content": "package webservice\n\nimport (\n\t\"html/template\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"path\"\n)\n\nvar (\n\tExamplesDir string\n\tTemplateDir string\n\tStaticDir   string\n)\n\nfunc indexHandler(w http.ResponseWriter, req *http.Request) {\n\tvar examples []string\n\tt, err := template.ParseFiles(path.Join(TemplateDir, \"index.tmpl\"))\n\tif err != nil {\n\t\tNewErrInternal(err, \"Cannot load template\").Report(w)\n\t}\n\td, err := ioutil.ReadDir(ExamplesDir)\n\tif err != nil {\n\t\tNewErrInternal(err, \"Cannot read examples\").Report(w)\n\t}\n\tfor _, f := range d {\n\t\tif f.IsDir() {\n\t\t\texamples = append(examples, f.Name())\n\t\t}\n\t}\n\n\tdata := struct {\n\t\tTitle    string\n\t\tExamples []string\n\t}{\n\t\tTitle:    \"GoInfer/Gong demo\",\n\t\tExamples: examples,\n\t}\n\terr = t.Execute(w, data)\n\tif err != nil {\n\t\tNewErrInternal(err, \"Template execute failed\").Report(w)\n\t}\n}\n\nfunc loadHandler(w http.ResponseWriter, req *http.Request) {\n\tb, err := ioutil.ReadAll(req.Body)\n\tif err != nil {\n\t\tNewErrInternal(err, \"Cannot read input\").Report(w)\n\t}\n\tif err := req.Body.Close(); err != nil {\n\t\tNewErrInternal(err, \"Cannot close request\").Report(w)\n\t}\n\tlog.Println(\"Load example:\", string(b))\n\tfile, err := os.Open(path.Join(ExamplesDir, string(b), \"main.go\"))\n\tif err != nil {\n\t\tNewErrInternal(err, \"Cannot open file\").Report(w)\n\t}\n\tio.Copy(w, file)\n}\n"
  },
  {
    "path": "webservice/migo.go",
    "content": "package webservice\n\nimport (\n\t\"encoding/json\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"net/http\"\n\n\t\"github.com/nickng/dingo-hunter/migoextract\"\n\t\"github.com/nickng/dingo-hunter/ssabuilder\"\n\t\"github.com/nickng/migo/v3/migoutil\"\n)\n\nfunc migoHandler(w http.ResponseWriter, req *http.Request) {\n\tb, err := ioutil.ReadAll(req.Body)\n\tif err != nil {\n\t\tNewErrInternal(err, \"Cannot read input Go source code\").Report(w)\n\t}\n\treq.Body.Close()\n\tconf, err := ssabuilder.NewConfigFromString(string(b))\n\tif err != nil {\n\t\tNewErrInternal(err, \"Cannot initialise SSA\").Report(w)\n\t}\n\tinfo, err := conf.Build()\n\tif err != nil {\n\t\tNewErrInternal(err, \"Cannot build SSA\").Report(w)\n\t}\n\textract, err := migoextract.New(info, ioutil.Discard)\n\tgo extract.Run()\n\n\tselect {\n\tcase <-extract.Error:\n\t\tNewErrInternal(err, \"MiGo type inference failed\").Report(w)\n\tcase <-extract.Done:\n\t\tlog.Println(\"MiGo: analysis completed in\", extract.Time)\n\t\tmigoutil.SimplifyProgram(extract.Env.MigoProg)\n\t}\n\n\treply := struct {\n\t\tMiGo string `json:\"MiGo\"`\n\t\tTime string `json:\"time\"`\n\t}{\n\t\tMiGo: extract.Env.MigoProg.String(),\n\t\tTime: extract.Time.String(),\n\t}\n\tjson.NewEncoder(w).Encode(&reply)\n}\n"
  },
  {
    "path": "webservice/play.go",
    "content": "package webservice\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"go/build\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"path/filepath\"\n\t\"time\"\n\n\t\"golang.org/x/tools/godoc/static\"\n\t\"golang.org/x/tools/playground/socket\"\n)\n\nconst basePkg = \"golang.org/x/tools/cmd/present\"\n\nvar scripts = []string{\"jquery.js\", \"jquery-ui.js\", \"playground.js\", \"play.js\"}\n\nfunc initPlayground(origin *url.URL) {\n\tp, err := build.Default.Import(basePkg, \"\", build.FindOnly)\n\tif err != nil {\n\t\tlog.Fatalf(\"Could not find gopresent files: %v\", err)\n\t}\n\tbasePath := p.Dir\n\n\tplayScript(basePath, \"SocketTransport\")\n\thttp.Handle(\"/socket\", socket.NewHandler(origin))\n}\n\nfunc playScript(root, transport string) {\n\tmodTime := time.Now()\n\tvar buf bytes.Buffer\n\tfor _, p := range scripts {\n\t\tif s, ok := static.Files[p]; ok {\n\t\t\tbuf.WriteString(s)\n\t\t\tcontinue\n\t\t}\n\t\tb, err := ioutil.ReadFile(filepath.Join(root, \"static\", p))\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tbuf.Write(b)\n\t}\n\tfmt.Fprintf(&buf, \"\\ninitPlayground(new %v());\\n\", transport)\n\tb := buf.Bytes()\n\thttp.HandleFunc(\"/play.js\", func(w http.ResponseWriter, r *http.Request) {\n\t\tw.Header().Set(\"Content-type\", \"application/javascript\")\n\t\thttp.ServeContent(w, r, \"\", modTime, bytes.NewReader(b))\n\t})\n}\n"
  },
  {
    "path": "webservice/server.go",
    "content": "package webservice\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"sync\"\n)\n\ntype Server struct {\n\tlistener net.Listener\n\tiface    string\n\tport     string\n\n\tlistenerMtx sync.Mutex\n}\n\nfunc NewServer(iface string, port string) *Server {\n\treturn &Server{\n\t\tiface: iface,\n\t\tport:  port,\n\t}\n}\n\nfunc (s *Server) Start() {\n\torigin := &url.URL{Scheme: \"http\", Host: net.JoinHostPort(s.iface, s.port)}\n\tinitPlayground(origin)\n\thttp.HandleFunc(\"/\", indexHandler)\n\tfs := http.FileServer(http.Dir(StaticDir))\n\thttp.Handle(\"/static/\", http.StripPrefix(\"/static/\", fs))\n\thttp.HandleFunc(\"/ssa\", ssaHandler)\n\thttp.HandleFunc(\"/load\", loadHandler)\n\thttp.HandleFunc(\"/cfsm\", cfsmHandler)\n\thttp.HandleFunc(\"/migo\", migoHandler)\n\thttp.HandleFunc(\"/gong\", gongHandler)\n\thttp.HandleFunc(\"/synthesis\", synthesisHandler)\n\n\tlog.Printf(\"Listening at %s\", s.URL())\n\t(&http.Server{}).Serve(s.Listener())\n}\n\nfunc (s *Server) Close() {\n\ts.Listener().Close()\n}\n\nfunc (s *Server) URL() string {\n\treturn fmt.Sprintf(\"http://%s/\", s.Listener().Addr())\n}\n\nfunc (s *Server) Listener() net.Listener {\n\ts.listenerMtx.Lock()\n\tdefer s.listenerMtx.Unlock()\n\n\tif s.listener != nil {\n\t\treturn s.listener\n\t}\n\n\tifaceAndPort := fmt.Sprintf(\"%v:%v\", s.iface, s.port)\n\tlistener, err := net.Listen(\"tcp4\", ifaceAndPort)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\ts.listener = listener\n\treturn s.listener\n}\n"
  },
  {
    "path": "webservice/ssa.go",
    "content": "package webservice\n\nimport (\n\t\"io/ioutil\"\n\t\"net/http\"\n\n\t\"github.com/nickng/dingo-hunter/ssabuilder\"\n)\n\nfunc ssaHandler(w http.ResponseWriter, req *http.Request) {\n\tb, err := ioutil.ReadAll(req.Body)\n\tif err != nil {\n\t\tNewErrInternal(err, \"Cannot read input Go source code\").Report(w)\n\t}\n\treq.Body.Close()\n\tconf, err := ssabuilder.NewConfigFromString(string(b))\n\tif err != nil {\n\t\tNewErrInternal(err, \"Cannot initialise SSA\").Report(w)\n\t}\n\tinfo, err := conf.Build()\n\tif err != nil {\n\t\tNewErrInternal(err, \"Cannot build SSA\").Report(w)\n\t}\n\tinfo.WriteTo(w)\n}\n"
  },
  {
    "path": "webservice/synthesis.go",
    "content": "package webservice\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path\"\n\t\"strings\"\n\t\"time\"\n)\n\nfunc synthesisHandler(w http.ResponseWriter, req *http.Request) {\n\tlog.Println(\"Running SMC check on snippet\")\n\tb, err := ioutil.ReadAll(req.Body)\n\tif err != nil {\n\t\tNewErrInternal(err, \"Cannot read input CFSM\").Report(w)\n\t}\n\treq.Body.Close()\n\tchanCFSMs := req.FormValue(\"chan\")\n\n\t// ---- Executables ----\n\tlog.Println(\"Finding required executables\")\n\tgmc, err := exec.LookPath(\"GMC\")\n\tif err != nil {\n\t\tNewErrInternal(err, \"Cannot find GMC executable (Check $PATH?)\").Report(w)\n\t}\n\tbg, err := exec.LookPath(\"BuildGlobal\")\n\tif err != nil {\n\t\tNewErrInternal(err, \"Cannot find BuildGobal executable (Check $PATH?)\").Report(w)\n\t}\n\tpetrify, err := exec.LookPath(\"petrify\")\n\tif err != nil {\n\t\tNewErrInternal(err, \"Cannot find petrify executable (Check $PATH?)\").Report(w)\n\t}\n\tdot, err := exec.LookPath(\"dot\")\n\tif err != nil {\n\t\tNewErrInternal(err, \"Cannot find dot executable (Check $PATH?)\").Report(w)\n\t}\n\n\t// ---- Output dirs/files ----\n\tbaseDir := path.Join(os.TempDir(), \"syn\")\n\terr = os.MkdirAll(baseDir, 0777)\n\tif err != nil {\n\t\tNewErrInternal(err, \"Cannot create temp dir\").Report(w)\n\t}\n\terr = os.MkdirAll(path.Join(baseDir, \"outputs\"), 0777)\n\tif err != nil {\n\t\tNewErrInternal(err, \"Cannot create final output dir\").Report(w)\n\t}\n\terr = os.Chdir(baseDir)\n\tif err != nil {\n\t\tNewErrInternal(err, \"Cannot chdir to temp dir\").Report(w)\n\t}\n\tfile, err := ioutil.TempFile(baseDir, \"cfsm\")\n\tif err != nil {\n\t\tNewErrInternal(err, \"Cannot create temp file for CFSM input\").Report(w)\n\t}\n\tdefer os.Remove(file.Name())\n\ttoPetrifyPath := path.Join(baseDir, \"outputs\", fmt.Sprintf(\"%s_toPetrify\", path.Base(file.Name())))\n\tpetriPath := path.Join(baseDir, \"default\")\n\tmachinesDotPath := path.Join(baseDir, \"outputs\", fmt.Sprintf(\"%s_machines.dot\", path.Base(file.Name())))\n\tglobalDotPath := path.Join(baseDir, \"outputs\", \"default_global.dot\")\n\n\tif _, err := file.Write(b); err != nil {\n\t\tNewErrInternal(err, \"Cannot write to temp file for CFSM input\").Report(w)\n\t}\n\tif err := file.Close(); err != nil {\n\t\tNewErrInternal(err, \"Cannot close temp file for CFSM input\").Report(w)\n\t}\n\n\t// Replace symbols\n\tre := strings.NewReplacer(\"AAA\", \"->\", \"CCC\", \",\", \"COCO\", \":\")\n\toutReplacer := strings.NewReplacer(\"True\", \"<span style='color: #87ff87; font-weight: bold'>True</span>\", \"False\", \"<span style='color: #ff005f; font-weight: bold'>False</span>\")\n\n\tstartTime := time.Now()\n\tgmcOut, err := exec.Command(gmc, file.Name(), chanCFSMs, \"+RTS\", \"-N\").CombinedOutput()\n\tif err != nil {\n\t\tlog.Printf(\"GMC execution failed: %v\\n\", err)\n\t}\n\tpetriOut, err := exec.Command(petrify, \"-dead\", \"-ip\", toPetrifyPath).CombinedOutput()\n\tif err != nil {\n\t\tlog.Printf(\"petrify execution failed: %v\\n\", err)\n\t}\n\tioutil.WriteFile(petriPath, []byte(re.Replace(string(petriOut))), 0664)\n\tbgOut, err := exec.Command(bg, petriPath).CombinedOutput()\n\tif err != nil {\n\t\tlog.Printf(\"BuildGlobal execution failed: %v\\n\", err)\n\t}\n\tlog.Println(\"BuildGlobal:\", string(bgOut))\n\n\texecTime := time.Now().Sub(startTime)\n\n\tmachinesSVG, err := exec.Command(dot, \"-Tsvg\", machinesDotPath).CombinedOutput()\n\tif err != nil {\n\t\tlog.Printf(\"dot execution failed for : %v\\n\", err)\n\t}\n\tglobalSVG, err := exec.Command(dot, \"-Tsvg\", globalDotPath).CombinedOutput()\n\tif err != nil {\n\t\tlog.Printf(\"dot execution failed for : %v\\n\", err)\n\t}\n\n\treply := struct {\n\t\tSMC      string `json:\"SMC\"`\n\t\tMachines string `json:\"Machines\"`\n\t\tGlobal   string `json:\"Global\"`\n\t\tTime     string `json:\"time\"`\n\t}{\n\t\tSMC:      outReplacer.Replace(string(gmcOut)),\n\t\tMachines: string(machinesSVG),\n\t\tGlobal:   string(globalSVG),\n\t\tTime:     execTime.String(),\n\t}\n\tlog.Println(\"Synthesis completed in\", execTime.String())\n\tjson.NewEncoder(w).Encode(&reply)\n}\n"
  }
]