Full Code of nickng/dingo-hunter for AI

master 6b8cec3641dc cached
171 files
397.9 KB
126.4k tokens
736 symbols
1 requests
Download .txt
Showing preview only (439K chars total). Download the full file or copy to clipboard to get everything.
Repository: nickng/dingo-hunter
Branch: master
Commit: 6b8cec3641dc
Files: 171
Total size: 397.9 KB

Directory structure:
gitextract_zp5j09dq/

├── .gitignore
├── .gitmodules
├── .travis.yml
├── LICENSE
├── README.md
├── cfsmextract/
│   ├── cfsmextract.go
│   ├── func.go
│   ├── gortn.go
│   ├── sesstype/
│   │   ├── cfsm.go
│   │   ├── dot.go
│   │   ├── nodes.go
│   │   ├── nodestack.go
│   │   ├── nodestack_test.go
│   │   ├── op_string.go
│   │   ├── sesstype.go
│   │   └── sesstype_test.go
│   ├── utils/
│   │   ├── defs.go
│   │   └── emptyvalue.go
│   ├── utils.go
│   └── visit.go
├── cmd/
│   ├── buildssa.go
│   ├── cfsms.go
│   ├── checkfair.go
│   ├── migo.go
│   ├── root.go
│   └── serve.go
├── doc.go
├── examples/
│   ├── altbit/
│   │   └── main.go
│   ├── branch-dependent-deadlock/
│   │   └── main.go
│   ├── channel-scoping-test/
│   │   └── main.go
│   ├── commaok/
│   │   └── main.go
│   ├── cond-recur/
│   │   └── main.go
│   ├── deadlocking-philosophers/
│   │   └── main.go
│   ├── dining-philosophers/
│   │   └── main.go
│   ├── factorial/
│   │   └── main.go
│   ├── fanin-pattern/
│   │   └── main.go
│   ├── fanin-pattern-commaok/
│   │   └── main.go
│   ├── fcall/
│   │   └── main.go
│   ├── forselect/
│   │   └── main.go
│   ├── giachino-concur14-dining-philosopher/
│   │   └── main.go
│   ├── giachino-concur14-factorial/
│   │   └── main.go
│   ├── github-golang-go-issue-12734/
│   │   └── main.go
│   ├── golang-blog-prime-sieve/
│   │   └── main.go
│   ├── infinite-prime-sieve/
│   │   └── main.go
│   ├── issue-10-close-wrong-migo-chan-name/
│   │   └── main.go
│   ├── issue-11-non-communicating-fn-call/
│   │   └── main.go
│   ├── jobsched/
│   │   └── main.go
│   ├── local-deadlock/
│   │   └── main.go
│   ├── local-deadlock-fixed/
│   │   └── main.go
│   ├── loop-variations/
│   │   └── main.go
│   ├── makechan-in-loop/
│   │   └── main.go
│   ├── md5/
│   │   └── main.go
│   ├── multi-makechan-same-var/
│   │   └── main.go
│   ├── multiple-files/
│   │   ├── main.go
│   │   └── x.go
│   ├── multiple-timeout/
│   │   └── main.go
│   ├── parallel-buffered-recursive-fibonacci/
│   │   └── main.go
│   ├── parallel-recursive-fibonacci/
│   │   └── main.go
│   ├── parallel-twoprocess-fibonacci/
│   │   └── main.go
│   ├── philo/
│   │   └── main.go
│   ├── popl17/
│   │   ├── alt-bit/
│   │   │   └── main.go
│   │   ├── concsys/
│   │   │   └── main.go
│   │   ├── cond-recur/
│   │   │   └── main.go
│   │   ├── dinephil/
│   │   │   └── main.go
│   │   ├── fact/
│   │   │   └── main.go
│   │   ├── fanin/
│   │   │   └── main.go
│   │   ├── fanin-alt/
│   │   │   └── main.go
│   │   ├── fib/
│   │   │   └── main.go
│   │   ├── fib-async/
│   │   │   └── main.go
│   │   ├── fixed/
│   │   │   └── main.go
│   │   ├── forselect/
│   │   │   └── main.go
│   │   ├── jobsched/
│   │   │   └── main.go
│   │   ├── mismatch/
│   │   │   └── main.go
│   │   └── sieve/
│   │       └── main.go
│   ├── popl17ae/
│   │   ├── close/
│   │   │   └── main.go
│   │   └── emptyselect/
│   │       └── main.go
│   ├── powsers/
│   │   └── powser1.go
│   ├── producer-consumer/
│   │   └── main.go
│   ├── ring-pattern/
│   │   └── main.go
│   ├── russ-cox-fizzbuzz/
│   │   └── main.go
│   ├── select-with-continuation/
│   │   └── main.go
│   ├── select-with-weak-mismatch/
│   │   └── main.go
│   ├── semaphores/
│   │   └── main.go
│   ├── send-recv-with-interfaces/
│   │   └── main.go
│   ├── simple/
│   │   └── main.go
│   ├── single-gortn-method-call/
│   │   └── main.go
│   ├── spawn-in-choice/
│   │   └── main.go
│   ├── squaring-cancellation/
│   │   └── main.go
│   ├── squaring-fanin/
│   │   └── main.go
│   ├── squaring-fanin-bad/
│   │   └── main.go
│   ├── squaring-pipeline/
│   │   └── main.go
│   ├── struct-done-channel/
│   │   └── main.go
│   └── timeout-behaviour/
│       └── main.go
├── fairness/
│   └── fairness.go
├── go.mod
├── go.sum
├── logwriter/
│   └── logwriter.go
├── main.go
├── migoextract/
│   ├── call.go
│   ├── chan.go
│   ├── closure.go
│   ├── commaok.go
│   ├── context.go
│   ├── datastructure.go
│   ├── error.go
│   ├── instance.go
│   ├── loop.go
│   ├── loopbound_string.go
│   ├── loopstack.go
│   ├── loopstate_string.go
│   ├── migoextract.go
│   ├── phi.go
│   ├── pointer.go
│   ├── print.go
│   ├── select.go
│   ├── storage.go
│   ├── tuple.go
│   └── visit.go
├── ssabuilder/
│   ├── callgraph/
│   │   └── callgraph.go
│   ├── channel.go
│   ├── errors.go
│   ├── pointer.go
│   ├── print.go
│   ├── ssabuild.go
│   └── util.go
├── static/
│   ├── script.js
│   └── style.css
├── talks/
│   ├── basic/
│   │   ├── concurrency.go
│   │   ├── fanin.go
│   │   └── select.go
│   ├── conf-cc-2016/
│   │   ├── deadlock-global.html
│   │   ├── fanin-global.html
│   │   ├── select.html
│   │   ├── ssa.html
│   │   └── type-inference.html
│   ├── conf-cc-2016.slide
│   ├── deadlock/
│   │   └── deadlock.go
│   ├── snippet/
│   │   └── proc.txt
│   ├── static/
│   │   ├── article.css
│   │   ├── dir.css
│   │   ├── dir.js
│   │   ├── jquery-ui.js
│   │   ├── slides.js
│   │   └── styles.css
│   ├── talk-golanguk-2016/
│   │   ├── calculi.html
│   │   ├── compat.html
│   │   ├── datatype.go
│   │   ├── deadlock-notdetected.go
│   │   ├── deadlock.go
│   │   ├── main-type.html
│   │   ├── proc.txt
│   │   ├── receiver-type.html
│   │   ├── sender-type.html
│   │   ├── video.html
│   │   └── work-type.html
│   ├── talk-golanguk-2016.slide
│   ├── talk-jan-2016.slide
│   └── templates/
│       ├── action.tmpl
│       ├── article.tmpl
│       ├── dir.tmpl
│       └── slides.tmpl
├── templates/
│   └── index.tmpl
└── webservice/
    ├── cfsm.go
    ├── errors.go
    ├── gong.go
    ├── handlers.go
    ├── migo.go
    ├── play.go
    ├── server.go
    ├── ssa.go
    └── synthesis.go

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so

# Folders
_obj
_test

# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out

*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*

_testmain.go

*.exe
*.test
*.prof

*.dot
*.pdf
*_cfsms

dingo-hunter
.*~


================================================
FILE: .gitmodules
================================================
[submodule "third_party/gmc-synthesis"]
	branch = dingo
	path = third_party/gmc-synthesis
	url = https://bitbucket.org/nickng/gmc-synthesis


================================================
FILE: .travis.yml
================================================
language: go
script:
    - go test -v ./...
addons:
    ssh_known_hosts:
        - bitbucket.org
notifications:
    slack:
        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=


================================================
FILE: LICENSE
================================================
                              Apache License
                        Version 2.0, January 2004
                     http://www.apache.org/licenses/

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

1. Definitions.

   "License" shall mean the terms and conditions for use, reproduction,
   and distribution as defined by Sections 1 through 9 of this document.

   "Licensor" shall mean the copyright owner or entity authorized by
   the copyright owner that is granting the License.

   "Legal Entity" shall mean the union of the acting entity and all
   other entities that control, are controlled by, or are under common
   control with that entity. For the purposes of this definition,
   "control" means (i) the power, direct or indirect, to cause the
   direction or management of such entity, whether by contract or
   otherwise, or (ii) ownership of fifty percent (50%) or more of the
   outstanding shares, or (iii) beneficial ownership of such entity.

   "You" (or "Your") shall mean an individual or Legal Entity
   exercising permissions granted by this License.

   "Source" form shall mean the preferred form for making modifications,
   including but not limited to software source code, documentation
   source, and configuration files.

   "Object" form shall mean any form resulting from mechanical
   transformation or translation of a Source form, including but
   not limited to compiled object code, generated documentation,
   and conversions to other media types.

   "Work" shall mean the work of authorship, whether in Source or
   Object form, made available under the License, as indicated by a
   copyright notice that is included in or attached to the work
   (an example is provided in the Appendix below).

   "Derivative Works" shall mean any work, whether in Source or Object
   form, that is based on (or derived from) the Work and for which the
   editorial revisions, annotations, elaborations, or other modifications
   represent, as a whole, an original work of authorship. For the purposes
   of this License, Derivative Works shall not include works that remain
   separable from, or merely link (or bind by name) to the interfaces of,
   the Work and Derivative Works thereof.

   "Contribution" shall mean any work of authorship, including
   the original version of the Work and any modifications or additions
   to that Work or Derivative Works thereof, that is intentionally
   submitted to Licensor for inclusion in the Work by the copyright owner
   or by an individual or Legal Entity authorized to submit on behalf of
   the copyright owner. For the purposes of this definition, "submitted"
   means any form of electronic, verbal, or written communication sent
   to the Licensor or its representatives, including but not limited to
   communication on electronic mailing lists, source code control systems,
   and issue tracking systems that are managed by, or on behalf of, the
   Licensor for the purpose of discussing and improving the Work, but
   excluding communication that is conspicuously marked or otherwise
   designated in writing by the copyright owner as "Not a Contribution."

   "Contributor" shall mean Licensor and any individual or Legal Entity
   on behalf of whom a Contribution has been received by Licensor and
   subsequently incorporated within the Work.

2. Grant of Copyright License. Subject to the terms and conditions of
   this License, each Contributor hereby grants to You a perpetual,
   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
   copyright license to reproduce, prepare Derivative Works of,
   publicly display, publicly perform, sublicense, and distribute the
   Work and such Derivative Works in Source or Object form.

3. Grant of Patent License. Subject to the terms and conditions of
   this License, each Contributor hereby grants to You a perpetual,
   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
   (except as stated in this section) patent license to make, have made,
   use, offer to sell, sell, import, and otherwise transfer the Work,
   where such license applies only to those patent claims licensable
   by such Contributor that are necessarily infringed by their
   Contribution(s) alone or by combination of their Contribution(s)
   with the Work to which such Contribution(s) was submitted. If You
   institute patent litigation against any entity (including a
   cross-claim or counterclaim in a lawsuit) alleging that the Work
   or a Contribution incorporated within the Work constitutes direct
   or contributory patent infringement, then any patent licenses
   granted to You under this License for that Work shall terminate
   as of the date such litigation is filed.

4. Redistribution. You may reproduce and distribute copies of the
   Work or Derivative Works thereof in any medium, with or without
   modifications, and in Source or Object form, provided that You
   meet the following conditions:

   (a) You must give any other recipients of the Work or
       Derivative Works a copy of this License; and

   (b) You must cause any modified files to carry prominent notices
       stating that You changed the files; and

   (c) You must retain, in the Source form of any Derivative Works
       that You distribute, all copyright, patent, trademark, and
       attribution notices from the Source form of the Work,
       excluding those notices that do not pertain to any part of
       the Derivative Works; and

   (d) If the Work includes a "NOTICE" text file as part of its
       distribution, then any Derivative Works that You distribute must
       include a readable copy of the attribution notices contained
       within such NOTICE file, excluding those notices that do not
       pertain to any part of the Derivative Works, in at least one
       of the following places: within a NOTICE text file distributed
       as part of the Derivative Works; within the Source form or
       documentation, if provided along with the Derivative Works; or,
       within a display generated by the Derivative Works, if and
       wherever such third-party notices normally appear. The contents
       of the NOTICE file are for informational purposes only and
       do not modify the License. You may add Your own attribution
       notices within Derivative Works that You distribute, alongside
       or as an addendum to the NOTICE text from the Work, provided
       that such additional attribution notices cannot be construed
       as modifying the License.

   You may add Your own copyright statement to Your modifications and
   may provide additional or different license terms and conditions
   for use, reproduction, or distribution of Your modifications, or
   for any such Derivative Works as a whole, provided Your use,
   reproduction, and distribution of the Work otherwise complies with
   the conditions stated in this License.

5. Submission of Contributions. Unless You explicitly state otherwise,
   any Contribution intentionally submitted for inclusion in the Work
   by You to the Licensor shall be under the terms and conditions of
   this License, without any additional terms or conditions.
   Notwithstanding the above, nothing herein shall supersede or modify
   the terms of any separate license agreement you may have executed
   with Licensor regarding such Contributions.

6. Trademarks. This License does not grant permission to use the trade
   names, trademarks, service marks, or product names of the Licensor,
   except as required for reasonable and customary use in describing the
   origin of the Work and reproducing the content of the NOTICE file.

7. Disclaimer of Warranty. Unless required by applicable law or
   agreed to in writing, Licensor provides the Work (and each
   Contributor provides its Contributions) on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
   implied, including, without limitation, any warranties or conditions
   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
   PARTICULAR PURPOSE. You are solely responsible for determining the
   appropriateness of using or redistributing the Work and assume any
   risks associated with Your exercise of permissions under this License.

8. Limitation of Liability. In no event and under no legal theory,
   whether in tort (including negligence), contract, or otherwise,
   unless required by applicable law (such as deliberate and grossly
   negligent acts) or agreed to in writing, shall any Contributor be
   liable to You for damages, including any direct, indirect, special,
   incidental, or consequential damages of any character arising as a
   result of this License or out of the use or inability to use the
   Work (including but not limited to damages for loss of goodwill,
   work stoppage, computer failure or malfunction, or any and all
   other commercial damages or losses), even if such Contributor
   has been advised of the possibility of such damages.

9. Accepting Warranty or Additional Liability. While redistributing
   the Work or Derivative Works thereof, You may choose to offer,
   and charge a fee for, acceptance of support, warranty, indemnity,
   or other liability obligations and/or rights consistent with this
   License. However, in accepting such obligations, You may act only
   on Your own behalf and on Your sole responsibility, not on behalf
   of any other Contributor, and only if You agree to indemnify,
   defend, and hold each Contributor harmless for any liability
   incurred by, or claims asserted against, such Contributor by reason
   of your accepting any such warranty or additional liability.

END OF TERMS AND CONDITIONS

APPENDIX: How to apply the Apache License to your work.

   To apply the Apache License to your work, attach the following
   boilerplate notice, with the fields enclosed by brackets "[]"
   replaced with your own identifying information. (Don't include
   the brackets!)  The text should be enclosed in the appropriate
   comment syntax for the file format. We also recommend that a
   file or class name and description of purpose be included on the
   same "printed page" as the copyright notice for easier
   identification within third-party archives.

Copyright [yyyy] [name of copyright owner]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.


================================================
FILE: README.md
================================================
# dingo-hunter [![Build Status](https://travis-ci.org/nickng/dingo-hunter.svg?branch=master)](https://travis-ci.org/nickng/dingo-hunter)

## Static analyser for finding Deadlocks in Go

This is a static analyser to model concurrency and find deadlocks in Go code.
The main purpose of this tool is to infer from Go source code its concurrency
model in either of the two formats:

 - Communicating Finite State Machines (CFSMs)
 - MiGo types

The inferred models are then passed to separate tools for formal analysis.
In both approaches, we apply a system known in the literature as
[**Session Types**](http://mrg.doc.ic.ac.uk/publications/multiparty-asynchronous-session-types/)
to look for potential communication mismatches to preempt potential deadlocks.

## Install

`dingo-hunter` can be installed by `go get`, go version `go1.7.1` is required.

    $ go get -u github.com/nickng/dingo-hunter

## Usage

There are two approaches (CFSMs and MiGo types) based on two research work.

### CFSMs approach

This approach generates CFSMs as models for goroutines spawned in the program,
the CFSMs are then passed to a synthesis tool to construct a global choreography
and check for validity (See [paper][cc16]).

First install the synthesis tool `gmc-synthesis` by checking out the submodule:

    $ cd $GOPATH/src/github.com/nickng/dingo-hunter; git submodule init; git submodule update
    $ cd third_party/gmc-synthesis

Follow `README` to install and build `gmc-synthesis`, i.e.

    $ cabal install MissingH split Graphalyze
    $ ./getpetrify # and install to /usr/local/ or in $PATH
    $ ghc -threaded GMC.hs --make && ghc --make BuildGlobal

To run CFSMs generation on `example/local-deadlock/main.go`:

    $ dingo-hunter cfsms --prefix deadlock example/local-deadlock/main.go

Output should say 2 channels, then run synthesis tool on extracted CFSMs

    $ cd third_party/gmc-synthesis
    $ ./runsmc inputs/deadlock_cfsms 2 # where 2 is the number of channels

The `SMC check` line indicates if the global graph satisfies SMC (i.e. safe) or not.

#### Limitations

  * Our tool currently support synchronous (unbuffered channel) communication only
  * Goroutines spawned after any communication operations must not depend on
    those communication. Our model assumes goroutines are spawned independenly.

### MiGo types approach

This approach generates MiGo types, a behavioural type introduced in [this work][popl17],
to check for safety and liveness by a restriction called *fencing* on channel
usage (See [paper][popl17]).

The checker for MiGo types is available at
[nickng/gong](https://github.com/nickng/gong), follow the instructions to build
the tool:

    $ git clone ssh://github.com/nickng/gong.git
    $ cd gong; ghc Gong.hs

To run MiGo types generation on `example/local-deadlock/main.go`:

    $ dingo-hunter migo example/local-deadlock/main.go --no-logging --output deadlock.migo
    $ /path/to/Gong -A deadlock.migo

#### Limitations

  * Channels as return values are not supported right now
  * Channel recv,ok test not possible to represent in MiGo (requires inspecting
    value but abstracted by types)

## Research publications

  * [Static Deadlock Detection for Concurrent Go by Global Session Graph Synthesis][cc16],
    Nicholas Ng and Nobuko Yoshida,
    Int'l Conference on Compiler Construction (CC 2016), ACM
  * [Fencing off Go: Liveness and Safety for Channel-based Programming][popl17],
    Julien Lange, Nicholas Ng, Bernardo Toninho and Nobuko Yoshida,
    ACM SIGPLAN Symposium on Principles of Programming Languages (POPL 2017), ACM

## Notes

This is a research prototype, and may not work for all Go source code. Please
file an issue for problems that look like a bug.

## License

  dingo-hunter is licensed under the [Apache License](http://www.apache.org/licenses/LICENSE-2.0)

[cc16]: http://dl.acm.org/citation.cfm?doid=2892208.2892232 "Static Deadlock Detection for Concurrent Go by Global Graph Synthesis"
[popl17]: http://dl.acm.org/citation.cfm?doiid=3009837.3009847 "Fencing off Go: Liveness and Safety for Channel-based Programming"


================================================
FILE: cfsmextract/cfsmextract.go
================================================
package cfsmextract

// This file contains only the functions needed to start the analysis
//  - Handle command line flags
//  - Set up session variables

import (
	"fmt"
	"go/types"
	"log"
	"os"
	"time"

	"github.com/nickng/dingo-hunter/cfsmextract/sesstype"
	"github.com/nickng/dingo-hunter/cfsmextract/utils"
	"github.com/nickng/dingo-hunter/ssabuilder"
	"golang.org/x/tools/go/ssa"
)

type CFSMExtract struct {
	SSA   *ssabuilder.SSAInfo
	Time  time.Duration
	Done  chan struct{}
	Error chan error

	session *sesstype.Session
	goQueue []*frame
	prefix  string
	outdir  string
}

func New(ssainfo *ssabuilder.SSAInfo, prefix, outdir string) *CFSMExtract {
	return &CFSMExtract{
		SSA:   ssainfo,
		Done:  make(chan struct{}),
		Error: make(chan error),

		session: sesstype.CreateSession(),
		goQueue: []*frame{},
		prefix:  prefix,
		outdir:  outdir,
	}
}

// Run function analyses main.main() then all the goroutines collected, and
// finally output the analysis results.
func (extract *CFSMExtract) Run() {
	startTime := time.Now()
	mainPkg := ssabuilder.MainPkg(extract.SSA.Prog)
	if mainPkg == nil {
		fmt.Fprintf(os.Stderr, "Error: 'main' package not found\n")
		os.Exit(1)
	}
	init := mainPkg.Func("init")
	main := mainPkg.Func("main")
	fr := makeToplevelFrame(extract)
	for _, pkg := range extract.SSA.Prog.AllPackages() {
		for _, memb := range pkg.Members {
			switch val := memb.(type) {
			case *ssa.Global:
				switch derefAll(val.Type()).(type) {
				case *types.Array:
					vd := utils.NewDef(val)
					fr.env.globals[val] = vd
					fr.env.arrays[vd] = make(Elems)

				case *types.Struct:
					vd := utils.NewDef(val)
					fr.env.globals[val] = vd
					fr.env.structs[vd] = make(Fields)

				case *types.Chan:
					var c *types.Chan
					vd := utils.NewDef(utils.EmptyValue{T: c})
					fr.env.globals[val] = vd

				default:
					fr.env.globals[val] = utils.NewDef(val)
				}
			}
		}
	}

	fmt.Fprintf(os.Stderr, "++ call.toplevel %s()\n", orange("init"))
	visitFunc(init, fr)
	if main == nil {
		fmt.Fprintf(os.Stderr, "Error: 'main()' function not found in 'main' package\n")
		os.Exit(1)
	}
	fmt.Fprintf(os.Stderr, "++ call.toplevel %s()\n", orange("main"))
	visitFunc(main, fr)

	fr.env.session.Types[fr.gortn.role] = fr.gortn.root

	var goFrm *frame
	for len(extract.goQueue) > 0 {
		goFrm, extract.goQueue = extract.goQueue[0], extract.goQueue[1:]
		fmt.Fprintf(os.Stderr, "\n%s\nLOCATION: %s%s\n", goFrm.fn.Name(), goFrm.gortn.role.Name(), loc(goFrm, goFrm.fn.Pos()))
		visitFunc(goFrm.fn, goFrm)
		goFrm.env.session.Types[goFrm.gortn.role] = goFrm.gortn.root
	}

	extract.Time = time.Since(startTime)
	extract.Done <- struct{}{}
}

// Session returns the session after extraction.
func (extract *CFSMExtract) Session() *sesstype.Session {
	return extract.session
}

func (extract *CFSMExtract) WriteOutput() {
	fmt.Printf(" ----- Results ----- \n%s\n", extract.session.String())

	sesstype.PrintNodeSummary(extract.session)

	dotFile, err := os.OpenFile(fmt.Sprintf("%s.dot", extract.prefix), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
	if err != nil {
		panic(err)
	}
	defer dotFile.Close()

	dot := sesstype.NewGraphvizDot(extract.session)
	if _, err = dot.WriteTo(dotFile); err != nil {
		panic(err)
	}

	os.MkdirAll(extract.outdir, 0750)
	cfsmPath := fmt.Sprintf("%s/%s_cfsms", extract.outdir, extract.prefix)
	cfsmFile, err := os.OpenFile(cfsmPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
	if err != nil {
		panic(err)
	}
	defer cfsmFile.Close()

	cfsms := sesstype.NewCFSMs(extract.session)
	if _, err := cfsms.WriteTo(cfsmFile); err != nil {
		log.Fatalf("Cannot write CFSMs to file: %v", err)
	}

	fmt.Fprintf(os.Stderr, "CFSMs written to %s\n", cfsmPath)
	cfsms.PrintSummary()
}


================================================
FILE: cfsmextract/func.go
================================================
package cfsmextract

import (
	"fmt"
	"go/types"
	"os"

	"github.com/nickng/dingo-hunter/cfsmextract/sesstype"
	"github.com/nickng/dingo-hunter/cfsmextract/utils"
	"golang.org/x/tools/go/ssa"
)

// VarKind specifies the type of utils.VarDef used in frame.local[]
type VarKind int

// VarKind definitions
const (
	Nothing     VarKind = iota // Not in frame.local[]
	Array                      // Array in heap
	Chan                       // Channel
	Struct                     // Struct in heap
	LocalArray                 // Array in frame
	LocalStruct                // Struct in frame
	Untracked                  // Not a tracked VarKind but in frame.local[]
)

// Captures are lists of VarDefs for closure captures
type Captures []*utils.Definition

// Tuples are lists of VarDefs from multiple return values
type Tuples []*utils.Definition

// Elems are maps from array indices (variable) to VarDefs
type Elems map[ssa.Value]*utils.Definition

// Fields are maps from struct fields (integer) to VarDefs
type Fields map[int]*utils.Definition

// Frame holds variables in current function scope
type frame struct {
	fn      *ssa.Function                   // Function ptr of callee
	locals  map[ssa.Value]*utils.Definition // Holds definitions of local registers
	arrays  map[*utils.Definition]Elems     // Array elements (Alloc local)
	structs map[*utils.Definition]Fields    // Struct fields (Alloc local)
	tuples  map[ssa.Value]Tuples            // Multiple return values as tuple
	phi     map[ssa.Value][]ssa.Value       // Phis
	recvok  map[ssa.Value]*sesstype.Chan    // Channel used in recvok
	retvals Tuples                          // Return values to pass back to parent
	defers  []*ssa.Defer                    // Deferred calls
	caller  *frame                          // Ptr to caller's frame, nil if main/ext
	env     *environ                        // Environment
	gortn   *goroutine                      // Current goroutine
}

// Environment: Variables/info available globally for all goroutines
type environ struct {
	session  *sesstype.Session
	extract  *CFSMExtract
	globals  map[ssa.Value]*utils.Definition      // Globals
	arrays   map[*utils.Definition]Elems          // Array elements
	structs  map[*utils.Definition]Fields         // Struct fields
	chans    map[*utils.Definition]*sesstype.Chan // Channels
	extern   map[ssa.Value]types.Type             // Values that originates externally, we are only sure of its type
	closures map[ssa.Value]Captures               // Closure captures
	selNode  map[ssa.Value]struct {               // Parent nodes of select
		parent   *sesstype.Node
		blocking bool
	}
	selIdx  map[ssa.Value]ssa.Value // Mapping from select index to select SSA Value
	selTest map[ssa.Value]struct {  // Records test for select-branch index
		idx int       // The index of the branch
		tpl ssa.Value // The SelectState tuple which the branch originates from
	}
	recvTest map[ssa.Value]*sesstype.Chan // Receive test
	ifparent *sesstype.NodeStack
}

func (env *environ) GetSessionChan(vd *utils.Definition) *sesstype.Chan {
	if ch, ok := env.session.Chans[vd]; ok {
		return &ch
	}
	panic(fmt.Sprintf("Channel %s undefined in session", vd.String()))
}

func makeToplevelFrame(extract *CFSMExtract) *frame {
	callee := &frame{
		fn:      nil,
		locals:  make(map[ssa.Value]*utils.Definition),
		arrays:  make(map[*utils.Definition]Elems),
		structs: make(map[*utils.Definition]Fields),
		tuples:  make(map[ssa.Value]Tuples),
		phi:     make(map[ssa.Value][]ssa.Value),
		recvok:  make(map[ssa.Value]*sesstype.Chan),
		retvals: make(Tuples, 0),
		defers:  make([]*ssa.Defer, 0),
		caller:  nil,
		env: &environ{
			session:  extract.session,
			extract:  extract,
			globals:  make(map[ssa.Value]*utils.Definition),
			arrays:   make(map[*utils.Definition]Elems),
			structs:  make(map[*utils.Definition]Fields),
			chans:    make(map[*utils.Definition]*sesstype.Chan),
			extern:   make(map[ssa.Value]types.Type),
			closures: make(map[ssa.Value]Captures),
			selNode: make(map[ssa.Value]struct {
				parent   *sesstype.Node
				blocking bool
			}),
			selIdx: make(map[ssa.Value]ssa.Value),
			selTest: make(map[ssa.Value]struct {
				idx int
				tpl ssa.Value
			}),
			recvTest: make(map[ssa.Value]*sesstype.Chan),
			ifparent: sesstype.NewNodeStack(),
		},
		gortn: &goroutine{
			role:    extract.session.GetRole("main"),
			root:    sesstype.NewLabelNode("main"),
			leaf:    nil,
			visited: make(map[*ssa.BasicBlock]sesstype.Node),
		},
	}
	callee.gortn.leaf = &callee.gortn.root

	return callee
}

func (caller *frame) callBuiltin(common *ssa.CallCommon) {
	builtin := common.Value.(*ssa.Builtin)
	if builtin.Name() == "close" {
		if len(common.Args) == 1 {
			if ch, ok := caller.env.chans[caller.locals[common.Args[0]]]; ok {
				fmt.Fprintf(os.Stderr, "++ call builtin %s(%s channel %s)\n", orange(builtin.Name()), green(common.Args[0].Name()), ch.Name())
				visitClose(*ch, caller)
			} else {
				panic("Builtin close() called with non-channel\n")
			}
		}
	} else if builtin.Name() == "copy" {
		dst := common.Args[0]
		src := common.Args[1]
		fmt.Fprintf(os.Stderr, "++ call builtin %s(%s <- %s)\n", orange("copy"), dst.Name(), src.Name())
		caller.locals[dst] = caller.locals[src]
		return
	} else {
		fmt.Fprintf(os.Stderr, "++ call builtin %s(", builtin.Name())
		for _, arg := range common.Args {
			fmt.Fprintf(os.Stderr, "%s", arg.Name())
		}
		fmt.Fprintf(os.Stderr, ") # TODO (handle builtin)\n")
	}
}

func (caller *frame) call(c *ssa.Call) {
	caller.callCommon(c, c.Common())
}

func (caller *frame) callCommon(call *ssa.Call, common *ssa.CallCommon) {
	switch fn := common.Value.(type) {
	case *ssa.Builtin:
		caller.callBuiltin(common)

	case *ssa.MakeClosure:
		// TODO(nickng) Handle calling closure
		fmt.Fprintf(os.Stderr, "   # TODO (handle closure) %s\n", fn.String())

	case *ssa.Function:
		if common.StaticCallee() == nil {
			panic("Call with nil CallCommon!")
		}

		callee := &frame{
			fn:      common.StaticCallee(),
			locals:  make(map[ssa.Value]*utils.Definition),
			arrays:  make(map[*utils.Definition]Elems),
			structs: make(map[*utils.Definition]Fields),
			tuples:  make(map[ssa.Value]Tuples),
			phi:     make(map[ssa.Value][]ssa.Value),
			recvok:  make(map[ssa.Value]*sesstype.Chan),
			retvals: make(Tuples, common.Signature().Results().Len()),
			defers:  make([]*ssa.Defer, 0),
			caller:  caller,
			env:     caller.env,   // Use the same env as caller
			gortn:   caller.gortn, // Use the same role as caller
		}

		fmt.Fprintf(os.Stderr, "++ call %s(", orange(common.StaticCallee().String()))
		callee.translate(common)
		fmt.Fprintf(os.Stderr, ")\n")

		if callee.isRecursive() {
			fmt.Fprintf(os.Stderr, "-- Recursive %s()\n", orange(common.StaticCallee().String()))
			callee.printCallStack()
		} else {
			if hasCode := visitFunc(callee.fn, callee); hasCode {
				caller.handleRetvals(call.Value(), callee)
			} else {
				caller.handleExtRetvals(call.Value(), callee)
			}
			fmt.Fprintf(os.Stderr, "-- return from %s (%d retvals)\n", orange(common.StaticCallee().String()), len(callee.retvals))
		}

	default:
		if !common.IsInvoke() {
			fmt.Fprintf(os.Stderr, "Unknown call type %v\n", common)
			return
		}

		switch vd, kind := caller.get(common.Value); kind {
		case Struct, LocalStruct:
			fmt.Fprintf(os.Stderr, "++ invoke %s.%s, type=%s\n", reg(common.Value), common.Method.String(), vd.Var.Type().String())
			// If dealing with interfaces, check that the method is invokable
			if iface, ok := common.Value.Type().Underlying().(*types.Interface); ok {
				if meth, _ := types.MissingMethod(vd.Var.Type(), iface, true); meth != nil {
					fmt.Fprintf(os.Stderr, "     ^ interface not fully implemented\n")
				} else {
					fn := findMethod(common.Value.Parent().Prog, common.Method, vd.Var.Type())
					if fn != nil {
						fmt.Fprintf(os.Stderr, "     ^ found function %s\n", fn.String())

						callee := &frame{
							fn:      fn,
							locals:  make(map[ssa.Value]*utils.Definition),
							arrays:  make(map[*utils.Definition]Elems),
							structs: make(map[*utils.Definition]Fields),
							tuples:  make(map[ssa.Value]Tuples),
							phi:     make(map[ssa.Value][]ssa.Value),
							recvok:  make(map[ssa.Value]*sesstype.Chan),
							retvals: make(Tuples, common.Signature().Results().Len()),
							defers:  make([]*ssa.Defer, 0),
							caller:  caller,
							env:     caller.env,   // Use the same env as caller
							gortn:   caller.gortn, // Use the same role as caller
						}

						common.Args = append([]ssa.Value{common.Value}, common.Args...)
						fmt.Fprintf(os.Stderr, "++ call %s(", orange(fn.String()))
						callee.translate(common)
						fmt.Fprintf(os.Stderr, ")\n")

						if callee.isRecursive() {
							fmt.Fprintf(os.Stderr, "-- Recursive %s()\n", orange(fn.String()))
							callee.printCallStack()
						} else {
							if hasCode := visitFunc(callee.fn, callee); hasCode {
								caller.handleRetvals(call.Value(), callee)
							} else {
								caller.handleExtRetvals(call.Value(), callee)
							}
							fmt.Fprintf(os.Stderr, "-- return from %s (%d retvals)\n", orange(fn.String()), len(callee.retvals))
						}

					} else {
						panic(fmt.Sprintf("Cannot call function: %s.%s is abstract (program not well-formed)", common.Value, common.Method.String()))
					}
				}
			} else {
				fmt.Fprintf(os.Stderr, "     ^ method %s.%s does not exist\n", reg(common.Value), common.Method.String())
			}

		default:
			fmt.Fprintf(os.Stderr, "++ invoke %s.%s\n", reg(common.Value), common.Method.String())
		}
	}
}

func findMethod(prog *ssa.Program, meth *types.Func, typ types.Type) *ssa.Function {
	if meth != nil {
		fmt.Fprintf(os.Stderr, "     ^ finding method for type: %s pkg: %s name: %s\n", typ.String(), meth.Pkg().Name(), meth.Name())
	}
	return prog.LookupMethod(typ, meth.Pkg(), meth.Name())
}

func (caller *frame) callGo(g *ssa.Go) {
	common := g.Common()
	goname := fmt.Sprintf("%s_%d", common.Value.Name(), int(g.Pos()))
	gorole := caller.env.session.GetRole(goname)

	callee := &frame{
		fn:      common.StaticCallee(),
		locals:  make(map[ssa.Value]*utils.Definition),
		arrays:  make(map[*utils.Definition]Elems),
		structs: make(map[*utils.Definition]Fields),
		tuples:  make(map[ssa.Value]Tuples),
		phi:     make(map[ssa.Value][]ssa.Value),
		recvok:  make(map[ssa.Value]*sesstype.Chan),
		retvals: make(Tuples, common.Signature().Results().Len()),
		defers:  make([]*ssa.Defer, 0),
		caller:  caller,
		env:     caller.env, // Use the same env as caller
		gortn: &goroutine{
			role:    gorole,
			root:    sesstype.NewLabelNode(goname),
			leaf:    nil,
			visited: make(map[*ssa.BasicBlock]sesstype.Node),
		},
	}
	callee.gortn.leaf = &callee.gortn.root

	fmt.Fprintf(os.Stderr, "@@ queue go %s(", common.StaticCallee().String())
	callee.translate(common)
	fmt.Fprintf(os.Stderr, ")\n")

	// TODO(nickng) Does not stop at recursive call.
	caller.env.extract.goQueue = append(caller.env.extract.goQueue, callee)
}

func (callee *frame) translate(common *ssa.CallCommon) {
	for i, param := range callee.fn.Params {
		argParent := common.Args[i]
		if param != argParent {
			if vd, ok := callee.caller.locals[argParent]; ok {
				callee.locals[param] = vd
			}
		}

		if i > 0 {
			fmt.Fprintf(os.Stderr, ", ")
		}

		fmt.Fprintf(os.Stderr, "%s:caller[%s] = %s", orange(param.Name()), reg(common.Args[i]), callee.locals[param].String())
		myVD := callee.locals[param] // VD of parameter (which are in callee.locals)

		// if argument is a channel
		if ch, ok := callee.env.chans[myVD]; ok {
			fmt.Fprintf(os.Stderr, " channel %s", (*ch).Name())
		} else if _, ok := callee.env.structs[myVD]; ok {
			fmt.Fprintf(os.Stderr, " struct")
		} else if _, ok := callee.env.arrays[myVD]; ok {
			fmt.Fprintf(os.Stderr, " array")
		} else if fields, ok := callee.caller.structs[myVD]; ok {
			// If param is local struct in caller, make local copy
			fmt.Fprintf(os.Stderr, " lstruct")
			callee.structs[myVD] = fields
		} else if elems, ok := callee.caller.arrays[myVD]; ok {
			// If param is local array in caller, make local copy
			fmt.Fprintf(os.Stderr, " larray")
			callee.arrays[myVD] = elems
		}
	}

	// Closure capture (copy from env.closures assigned in MakeClosure).
	if captures, isClosure := callee.env.closures[common.Value]; isClosure {
		for idx, fv := range callee.fn.FreeVars {
			callee.locals[fv] = captures[idx]
			fmt.Fprintf(os.Stderr, ", capture %s = %s", fv.Name(), captures[idx].String())
		}
	}
}

// handleRetvals looks up and stores return value from function calls.
// Nothing will be done if there are no return values from the function.
func (caller *frame) handleRetvals(returned ssa.Value, callee *frame) {
	if len(callee.retvals) > 0 {
		if len(callee.retvals) == 1 {
			// Single return value (callee.retvals[0])
			caller.locals[returned] = callee.retvals[0]
		} else {
			// Multiple return values (callee.retvals tuple)
			caller.tuples[returned] = callee.retvals
		}
	}
}

func (callee *frame) get(v ssa.Value) (*utils.Definition, VarKind) {
	if vd, ok := callee.locals[v]; ok {
		if _, ok := callee.env.arrays[vd]; ok {
			return vd, Array
		}
		if _, ok := callee.arrays[vd]; ok {
			return vd, LocalArray
		}
		if _, ok := callee.env.chans[vd]; ok {
			return vd, Chan
		}
		if _, ok := callee.env.structs[vd]; ok {
			return vd, Struct
		}
		if _, ok := callee.structs[vd]; ok {
			return vd, LocalStruct
		}
		return vd, Untracked
	} else if vs, ok := callee.phi[v]; ok {
		for i := len(vs) - 1; i >= 0; i-- {
			if chVd, defined := callee.locals[vs[i]]; defined {
				return chVd, Chan
			}
		}
	}
	return nil, Nothing
}

// handleExtRetvals looks up and stores return value from (ext) function calls.
// Ext functions have no code (no body to analyse) and unlike normal values,
// the return values/tuples are stored until they are referenced.
func (caller *frame) handleExtRetvals(returned ssa.Value, callee *frame) {
	// Since there are no code for the function, we use the function
	// signature to see if any of these are channels.
	// XXX We don't know where these come from so we put them in extern.
	resultsLen := callee.fn.Signature.Results().Len()
	if resultsLen > 0 {
		caller.env.extern[returned] = callee.fn.Signature.Results()
		if resultsLen == 1 {
			fmt.Fprintf(os.Stderr, "-- Return from %s (builtin/ext) with a single value\n", callee.fn.String())
			if _, ok := callee.fn.Signature.Results().At(0).Type().(*types.Chan); ok {
				vardef := utils.NewDef(returned)
				ch := caller.env.session.MakeExtChan(vardef, caller.gortn.role)
				caller.env.chans[vardef] = &ch
				fmt.Fprintf(os.Stderr, "-- Return value from %s (builtin/ext) is a channel %s (ext)\n", callee.fn.String(), (*caller.env.chans[vardef]).Name())
			}
		} else {
			fmt.Fprintf(os.Stderr, "-- Return from %s (builtin/ext) with %d-tuple\n", callee.fn.String(), resultsLen)
		}
	}
}

func (callee *frame) isRecursive() bool {
	var tracebackFns []*ssa.Function
	foundFr := callee
	for fr := callee.caller; fr != nil; fr = fr.caller {
		tracebackFns = append(tracebackFns, fr.fn)
		if fr.fn == callee.fn {
			foundFr = fr
			break
		}
	}
	// If same function is not found, not recursive
	if foundFr == callee {
		return false
	}

	// Otherwise try to trace back with foundFr and is recursive if all matches
	for _, fn := range tracebackFns {
		if foundFr == nil || foundFr.fn != fn {
			return false
		}
		foundFr = foundFr.caller
	}
	return true
}

func (callee *frame) printCallStack() {
	curFr := callee
	for curFr != nil && curFr.fn != nil {
		fmt.Fprintf(os.Stderr, "Called by: %s()\n", curFr.fn.String())
		curFr = curFr.caller
	}
}

func (callee *frame) updateDefs(vdOld, vdNew *utils.Definition) {
	for def, array := range callee.arrays {
		for k, v := range array {
			if v == vdOld {
				callee.arrays[def][k] = vdNew
			}
		}
	}
	for def, array := range callee.env.arrays {
		for k, v := range array {
			if v == vdOld {
				callee.env.arrays[def][k] = vdNew
			}
		}
	}
	for def, struc := range callee.structs {
		for i, field := range struc {
			if field == vdOld {
				callee.structs[def][i] = vdNew
			}
		}
	}
	for def, struc := range callee.env.structs {
		for i, field := range struc {
			if field == vdOld {
				callee.env.structs[def][i] = vdNew
			}
		}
	}
}


================================================
FILE: cfsmextract/gortn.go
================================================
package cfsmextract

import (
	"github.com/nickng/dingo-hunter/cfsmextract/sesstype"
	"golang.org/x/tools/go/ssa"
)

type goroutine struct {
	role    sesstype.Role
	root    sesstype.Node
	leaf    *sesstype.Node
	visited map[*ssa.BasicBlock]sesstype.Node
}

// Append a session type node to current goroutine.
func (gortn *goroutine) AddNode(node sesstype.Node) {
	if gortn.leaf == nil {
		panic("AddNode: leaf cannot be nil")
	}

	newLeaf := (*gortn.leaf).Append(node)
	gortn.leaf = &newLeaf
}


================================================
FILE: cfsmextract/sesstype/cfsm.go
================================================
package sesstype

import (
	"fmt"
	"io"
	"log"

	"github.com/nickng/cfsm"
)

// STOP is the 'close' message.
const STOP = "STOP"

// CFSMs captures a CFSM system syserated from a Session.
type CFSMs struct {
	Sys    *cfsm.System
	Chans  map[Role]*cfsm.CFSM
	Roles  map[Role]*cfsm.CFSM
	States map[*cfsm.CFSM]map[string]*cfsm.State
}

func NewCFSMs(s *Session) *CFSMs {
	sys := &CFSMs{
		Sys:    cfsm.NewSystem(),
		Chans:  make(map[Role]*cfsm.CFSM),
		Roles:  make(map[Role]*cfsm.CFSM),
		States: make(map[*cfsm.CFSM]map[string]*cfsm.State),
	}
	for _, c := range s.Chans {
		m := sys.Sys.NewMachine()
		m.Comment = c.Name()
		sys.Chans[c] = m
		defer sys.chanToMachine(c, c.Type().String(), m)
	}
	for role, root := range s.Types {
		m := sys.Sys.NewMachine()
		m.Comment = role.Name()
		sys.Roles[role] = m
		sys.States[m] = make(map[string]*cfsm.State)
		sys.rootToMachine(role, root, m)
		if m.IsEmpty() {
			log.Println("Machine", m.ID, "is empty")
			sys.Sys.RemoveMachine(m.ID)
			delete(sys.Roles, role)
			delete(sys.States, m)
		}
	}
	return sys
}

// WriteTo implementers io.WriterTo interface.
func (sys *CFSMs) WriteTo(w io.Writer) (int64, error) {
	n, err := w.Write([]byte(sys.Sys.String()))
	return int64(n), err
}

// PrintSummary shows the statistics of the CFSM syseration.
func (sys *CFSMs) PrintSummary() {
	fmt.Printf("Total of %d CFSMs (%d are channels)\n",
		len(sys.Roles)+len(sys.Chans), len(sys.Chans))
	for r, m := range sys.Chans {
		fmt.Printf("\t%d\t= %s (channel)\n", m.ID, r.Name())
	}
	for r, m := range sys.Roles {
		fmt.Printf("\t%d\t= %s\n", m.ID, r.Name())
	}
}

func (sys *CFSMs) rootToMachine(role Role, root Node, m *cfsm.CFSM) {
	q0 := m.NewState()
	sys.nodeToMachine(role, root, q0, m)
	m.Start = q0
}

func (sys *CFSMs) nodeToMachine(role Role, node Node, q0 *cfsm.State, m *cfsm.CFSM) {
	switch node := node.(type) {
	case *SendNode:
		to, ok := sys.Chans[node.To()]
		if !ok {
			log.Fatal("Cannot Send to unknown channel", node.To().Name())
		}
		tr := cfsm.NewSend(to, node.To().Type().String())
		var qSent *cfsm.State
		if sys.isSelfLoop(m, q0, node) {
			qSent = q0
		} else {
			qSent = m.NewState()
			for _, c := range node.Children() {
				sys.nodeToMachine(role, c, qSent, m)
			}
		}
		tr.SetNext(qSent)
		q0.AddTransition(tr)

	case *RecvNode:
		from, ok := sys.Chans[node.From()]
		if !ok {
			log.Fatal("Cannot Recv from unknown channel", node.From().Name())
		}
		msg := node.From().Type().String()
		if node.Stop() {
			msg = STOP
		}
		tr := cfsm.NewRecv(from, msg)
		var qRcvd *cfsm.State
		if sys.isSelfLoop(m, q0, node) {
			qRcvd = q0
		} else {
			qRcvd = m.NewState()
			for _, c := range node.Children() {
				sys.nodeToMachine(role, c, qRcvd, m)
			}
		}
		tr.SetNext(qRcvd)
		q0.AddTransition(tr)

	case *EndNode:
		ch, ok := sys.Chans[node.Chan()]
		if !ok {
			log.Fatal("Cannot Close unknown channel", node.Chan().Name())
		}
		tr := cfsm.NewSend(ch, STOP)
		qEnd := m.NewState()
		for _, c := range node.Children() {
			sys.nodeToMachine(role, c, qEnd, m)
		}
		tr.SetNext(qEnd)
		q0.AddTransition(tr)

	case *NewChanNode, *EmptyBodyNode: // Skip
		for _, c := range node.Children() {
			sys.nodeToMachine(role, c, q0, m)
		}

	case *LabelNode:
		sys.States[m][node.Name()] = q0
		for _, c := range node.Children() {
			sys.nodeToMachine(role, c, q0, m)
		}

	case *GotoNode:
		qTarget := sys.States[m][node.Name()]
		for _, c := range node.Children() {
			sys.nodeToMachine(role, c, qTarget, m)
		}

	default:
		log.Fatalf("Unhandled node type %T", node)
	}
}

func (sys *CFSMs) chanToMachine(ch Role, T string, m *cfsm.CFSM) {
	q0 := m.NewState()
	qEnd := m.NewState()
	for _, machine := range sys.Roles {
		q1 := m.NewState()
		// q0 -- Recv --> q1
		tr0 := cfsm.NewRecv(machine, T)
		tr0.SetNext(q1)
		q0.AddTransition(tr0)
		// q1 -- Send --> q0
		for _, machine2 := range sys.Roles {
			if machine.ID != machine2.ID {
				tr1 := cfsm.NewSend(machine2, T)
				tr1.SetNext(q0)
				q1.AddTransition(tr1)
			}
		}
		// q0 -- STOP --> qEnd (same qEnd)
		tr2 := cfsm.NewRecv(machine, STOP)
		tr2.SetNext(qEnd)
		qEnd.AddTransition(tr2)
		// qEnd -- STOP --> qEnd
		for _, machine2 := range sys.Roles {
			if machine.ID != machine2.ID {
				tr3 := cfsm.NewSend(machine2, STOP)
				tr3.SetNext(qEnd)
				qEnd.AddTransition(tr3)
			}
		}
	}
	m.Start = q0
}

// isSelfLoop returns true if the action of node is a self-loop
// i.e. the state before and after the transition is the same.
func (sys *CFSMs) isSelfLoop(m *cfsm.CFSM, q0 *cfsm.State, node Node) bool {
	if len(node.Children()) == 1 {
		if gotoNode, ok := node.Child(0).(*GotoNode); ok {
			if loopback, ok := sys.States[m][gotoNode.Name()]; ok {
				return loopback == q0
			}
		}
	}
	return false
}


================================================
FILE: cfsmextract/sesstype/dot.go
================================================
package sesstype

import (
	"fmt"
	"io"
	"log"

	"github.com/awalterschulze/gographviz"
)

// GraphvizDot reprents a new graphviz dot graph.
type GraphvizDot struct {
	Graph      *gographviz.Escape
	Count      int
	LabelNodes map[string]string
}

// NewGraphvizDot creates a new graphviz dot graph from a session.
func NewGraphvizDot(s *Session) *GraphvizDot {
	dot := &GraphvizDot{
		Graph:      gographviz.NewEscape(),
		Count:      0,
		LabelNodes: make(map[string]string),
	}
	dot.Graph.SetDir(true)
	dot.Graph.SetName("G")

	for role, root := range s.Types {
		sg := gographviz.NewSubGraph("\"cluster_" + role.Name() + "\"")
		if root != nil {
			dot.visitNode(root, sg, nil)
		}
		dot.Graph.AddSubGraph(dot.Graph.Name, sg.Name, nil)
	}
	return dot
}

// WriteTo implements io.WriterTo interface.
func (dot *GraphvizDot) WriteTo(w io.Writer) (int64, error) {
	n, err := w.Write([]byte(dot.Graph.String()))
	return int64(n), err
}

func (dot *GraphvizDot) nodeToDotNode(node Node) *gographviz.Node {
	switch node := node.(type) {
	case *LabelNode:
		defer func() { dot.Count++ }()
		dot.LabelNodes[node.Name()] = fmt.Sprintf("label%d", dot.Count)
		attrs, err := gographviz.NewAttrs(map[string]string{
			"label": fmt.Sprintf("\"%s\"", node.String()),
			"shape": "plaintext,",
		})
		if err != nil {
			log.Fatal(err)
		}
		dotNode := gographviz.Node{
			Name:  dot.LabelNodes[node.Name()],
			Attrs: attrs,
		}
		return &dotNode

	case *NewChanNode:
		defer func() { dot.Count++ }()
		attrs, err := gographviz.NewAttrs(map[string]string{
			"label": fmt.Sprintf("Channel %s Type:%s", node.Chan().Name(), node.Chan().Type()),
			"shape": "rect",
			"color": "red",
		})
		if err != nil {
			log.Fatal(err)
		}
		return &gographviz.Node{
			Name:  fmt.Sprintf("%s%d", node.Kind(), dot.Count),
			Attrs: attrs,
		}

	case *SendNode:
		defer func() { dot.Count++ }()
		style := "solid"
		desc := ""
		if node.IsNondet() {
			style = "dashed"
			desc = " nondet"
		}
		attrs, err := gographviz.NewAttrs(map[string]string{
			"label": fmt.Sprintf("Send %s%s", node.To().Name(), desc),
			"shape": "rect",
			"style": style,
		})
		if err != nil {
			log.Fatal(err)
		}
		return &gographviz.Node{
			Name:  fmt.Sprintf("%s%d", node.Kind(), dot.Count),
			Attrs: attrs,
		}

	case *RecvNode:
		defer func() { dot.Count++ }()
		style := "solid"
		desc := ""
		if node.IsNondet() {
			style = "dashed"
			desc = " nondet"
		}
		attrs, err := gographviz.NewAttrs(map[string]string{
			"label": fmt.Sprintf("Recv %s%s", node.From().Name(), desc),
			"shape": "rect",
			"style": style,
		})
		if err != nil {
			log.Fatal(err)
		}
		return &gographviz.Node{
			Name:  fmt.Sprintf("%s%d", node.Kind(), dot.Count),
			Attrs: attrs,
		}

	case *GotoNode:
		return nil // No new node to create

	default:
		defer func() { dot.Count++ }()
		attrs, err := gographviz.NewAttrs(map[string]string{
			"label": fmt.Sprintf("\"%s\"", node.String()),
			"shape": "rect",
		})
		if err != nil {
			log.Fatal(err)
		}
		dotNode := gographviz.Node{
			Name:  fmt.Sprintf("%s%d", node.Kind(), dot.Count),
			Attrs: attrs,
		}
		return &dotNode
	}
}

// visitNode Creates a dot Node and from it create a subgraph of children.
// Returns head of the subgraph.
func (dot *GraphvizDot) visitNode(node Node, subgraph *gographviz.SubGraph, parent *gographviz.Node) *gographviz.Node {
	dotNode := dot.nodeToDotNode(node)

	if dotNode == nil { // GotoNode
		gtn := node.(*GotoNode)
		dot.Graph.AddEdge(parent.Name, dot.LabelNodes[gtn.Name()], true, nil)
		for _, child := range node.Children() {
			dot.visitNode(child, subgraph, parent)
		}
		return parent // GotoNode's children are children of parent. So return parent.
	}

	attrs := make(map[string]string)
	for k, v := range dotNode.Attrs {
		attrs[string(k)] = v
	}
	dot.Graph.AddNode(subgraph.Name, dotNode.Name, attrs)
	if parent != nil { // Parent is not toplevel
		dot.Graph.AddEdge(parent.Name, dotNode.Name, true, nil)
	}
	for _, child := range node.Children() {
		dot.visitNode(child, subgraph, dotNode)
	}

	if dotNode == nil {
		panic("Cannot return nil dotNode")
	}

	return dotNode
}


================================================
FILE: cfsmextract/sesstype/nodes.go
================================================
package sesstype

import (
	"fmt"
)

func CountNodes(root Node) int {
	total := 1
	for _, child := range root.Children() {
		total += CountNodes(child)
	}
	return total
}

func SessionCountNodes(session *Session) map[string]int {
	m := make(map[string]int)
	for r, t := range session.Types {
		m[r.Name()] = CountNodes(t)
	}
	return m
}

func PrintNodeSummary(session *Session) {
	counts := SessionCountNodes(session)
	fmt.Printf("Total of nodes per role (%d roles)\n", len(counts))
	for role, n := range counts {
		fmt.Printf("\t%d\t: %s\n", n, role)
	}
}


================================================
FILE: cfsmextract/sesstype/nodestack.go
================================================
package sesstype // import "github.com/nickng/dingo-hunter/cfsmextract/sesstype"

// NodeStack is a stack for sesstype.Node
type NodeStack struct {
	nodes []Node
	count int
}

// Push pushes a sesstype.Node to the stack.
func (s *NodeStack) Push(node Node) {
	s.nodes = append(s.nodes[:s.count], node)
	s.count++
}

// Pop removes a sesstype.Node from the stack.
func (s *NodeStack) Pop() {
	if s.count <= 0 {
		panic("Cannot pop empty stack")
	}
	s.count--
}

// Top returns the sesstype.Node at the top of the stack.
func (s *NodeStack) Top() Node {
	if s.count <= 0 {
		return nil
	}
	return s.nodes[s.count-1]
}

// Size returns number of sesstype.Node on the stack.
func (s *NodeStack) Size() int {
	return s.count
}

// String returns a String representing the stack.
func (s *NodeStack) String() string {
	str := "["
	for i := s.count - 1; i >= 0; i-- {
		str += s.nodes[i].String()
		if i != 0 {
			str += ", "
		}
	}
	str += "]"
	return str
}

// NewNodeStack returns a new NodeStack instance.
func NewNodeStack() *NodeStack {
	return &NodeStack{}
}


================================================
FILE: cfsmextract/sesstype/nodestack_test.go
================================================
package sesstype

import (
	"testing"
)

func TestNewStack(t *testing.T) {
	ns := NewNodeStack()
	ns.Push(NewLabelNode("TEST"))
	ns.Push(NewLabelNode("TEST2"))
	ns.Push(NewLabelNode("TEST3"))
	l := ns.Top()
	if l.String() != "TEST3" {
		t.Fail()
	}
	ns.Pop()
	l2 := ns.Top()
	if l2.String() != "TEST2" {
		t.Fail()
	}
	ns.Pop()
	l3 := ns.Top()
	if l3.String() != "TEST" {
		t.Fail()
	}
	ns.Pop()
}


================================================
FILE: cfsmextract/sesstype/op_string.go
================================================
// generated by stringer -type=op; DO NOT EDIT

package sesstype

import "fmt"

const _op_name = "NoOpNewChanOpSendOpRecvOpEndOp"

var _op_index = [...]uint8{0, 4, 13, 19, 25, 30}

func (i op) String() string {
	if i < 0 || i >= op(len(_op_index)-1) {
		return fmt.Sprintf("op(%d)", i)
	}
	return _op_name[_op_index[i]:_op_index[i+1]]
}


================================================
FILE: cfsmextract/sesstype/sesstype.go
================================================
// Package sesstype encapsulates representation of session types
// As opposed to role-based session types, this representation is channel-based.
// In particular, sending and receiving both keep track of the role and
// channel involved.
package sesstype // import "github.com/nickng/dingo-hunter/cfsmextract/sesstype"

import (
	"fmt"
	"go/types"

	"github.com/nickng/dingo-hunter/cfsmextract/utils"
	"golang.org/x/tools/go/ssa"
)

//go:generate stringer -type=op
type op int

// Chan is a typed channel in a session.
type Chan struct {
	def    *utils.Definition
	role   Role
	extern bool
}

// Return a name of channel.
func (ch Chan) Name() string {
	fullname := fmt.Sprintf("%s", ch.def.String())
	if ch.extern {
		return fullname + "*"
	}
	return fullname
}

// Return the payload type of channel.
func (ch Chan) Type() types.Type {
	if c, ok := ch.def.Var.Type().(*types.Chan); ok {
		return c.Elem()
	}
	panic("Not channel " + ch.def.Var.String())
}
func (ch Chan) Role() Role       { return ch.role }
func (ch Chan) Value() ssa.Value { return ch.def.Var }

// Role in a session (main or goroutine).
type Role interface {
	Name() string
}

type role struct {
	name string
}

func (r *role) Name() string { return r.name }

// Different operations/actions available in session.
const (
	NoOp op = iota
	NewChanOp
	SendOp
	RecvOp
	EndOp
)

// A Node in the session graph.
type Node interface {
	Kind() op               // For checking type without type switching
	Child(index int) Node   // Gets child at index
	Append(child Node) Node // Returns new child for chaining
	Children() []Node       // Returns whole slice
	String() string
}

// Session is a container of session graph nodes, also holds information about
// channels and roles in the current session.
type Session struct {
	Types map[Role]Node              // Root Node for each Role
	Chans map[*utils.Definition]Chan // Actual channels are stored here
	Roles map[string]Role            // Actual roles are stored here
}

// CreateSession initialises a new empty Session.
func CreateSession() *Session {
	return &Session{
		Types: make(map[Role]Node),
		Chans: make(map[*utils.Definition]Chan),
		Roles: make(map[string]Role),
	}
}

// GetRole returns or create (if empty) a new session role using given name.
func (s *Session) GetRole(name string) Role { // Get or create role
	if _, found := s.Roles[name]; !found {
		s.Roles[name] = &role{name: name}
	}
	return s.Roles[name]
}

// MakeChan creates and stores a new session channel created.
func (s *Session) MakeChan(v *utils.Definition, r Role) Chan {
	s.Chans[v] = Chan{
		def:    v,
		role:   r,
		extern: false,
	}
	return s.Chans[v]
}

// MakeExtChan creates and stores a new channel and mark as externally created.
func (s *Session) MakeExtChan(v *utils.Definition, r Role) Chan {
	s.Chans[v] = Chan{
		def:    v,
		role:   r,
		extern: true,
	}
	return s.Chans[v]
}

// NewChanNode represents creation of new channel
type NewChanNode struct {
	ch       Chan
	children []Node
}

func (nc *NewChanNode) Kind() op   { return NewChanOp }
func (nc *NewChanNode) Chan() Chan { return nc.ch }
func (nc *NewChanNode) Append(n Node) Node {
	nc.children = append(nc.children, n)
	return n
}
func (nc *NewChanNode) Child(i int) Node { return nc.children[i] }
func (nc *NewChanNode) Children() []Node { return nc.children }
func (nc *NewChanNode) String() string {
	return fmt.Sprintf("NewChan %s of type %s", nc.ch.Name(), nc.ch.Type().String())
}

// SendNode represents a send.
type SendNode struct {
	sndr     Role       // Sender
	dest     Chan       // Destination
	nondet   bool       // Is this non-deterministic?
	t        types.Type // Datatype
	children []Node
}

func (s *SendNode) Kind() op       { return SendOp }
func (s *SendNode) Sender() Role   { return s.sndr }
func (s *SendNode) To() Chan       { return s.dest }
func (s *SendNode) IsNondet() bool { return s.nondet }
func (s *SendNode) Append(n Node) Node {
	s.children = append(s.children, n)
	return n
}
func (s *SendNode) Child(i int) Node { return s.children[i] }
func (s *SendNode) Children() []Node { return s.children }
func (s *SendNode) String() string {
	var nd string
	if s.nondet {
		nd = "nondet "
	}
	return fmt.Sprintf("Send %s→ᶜʰ%s %s", s.sndr.Name(), s.dest.Name(), nd)
}

// RecvNode represents a receive.
type RecvNode struct {
	orig     Chan       // Originates from
	rcvr     Role       // Received by
	nondet   bool       // Is this non-deterministic?
	t        types.Type // Datatype
	stop     bool       // Stop message only?
	children []Node
}

func (r *RecvNode) Kind() op       { return RecvOp }
func (r *RecvNode) Receiver() Role { return r.rcvr }
func (r *RecvNode) From() Chan     { return r.orig }
func (r *RecvNode) IsNondet() bool { return r.nondet }
func (r *RecvNode) Stop() bool     { return r.stop }
func (r *RecvNode) Append(node Node) Node {
	r.children = append(r.children, node)
	return node
}
func (r *RecvNode) Child(index int) Node { return r.children[index] }
func (r *RecvNode) Children() []Node     { return r.children }
func (r *RecvNode) String() string {
	var nd string
	if r.nondet {
		nd = "nondet "
	}
	if r.t == nil {
		return fmt.Sprintf("Recv END %s←ᶜʰ%s%s", r.rcvr.Name(), r.orig.Name(), nd)
	}
	return fmt.Sprintf("Recv %s←ᶜʰ%s%s", r.rcvr.Name(), r.orig.Name(), nd)
}

// LabelNode makes a placeholder for loop/jumping
type LabelNode struct {
	name     string
	children []Node
}

func (l *LabelNode) Kind() op     { return NoOp }
func (l *LabelNode) Name() string { return l.name }
func (l *LabelNode) Append(n Node) Node {
	l.children = append(l.children, n)
	return n
}
func (l *LabelNode) Child(i int) Node { return l.children[i] }
func (l *LabelNode) Children() []Node { return l.children }
func (l *LabelNode) String() string   { return fmt.Sprintf("%s", l.name) }

// GotoNode represents a jump (edge to existing LabelNode)
type GotoNode struct {
	name     string
	children []Node
}

func (g *GotoNode) Kind() op     { return NoOp }
func (g *GotoNode) Name() string { return g.name }
func (g *GotoNode) Append(n Node) Node {
	g.children = append(g.children, n)
	return n
}
func (g *GotoNode) Child(i int) Node { return g.children[i] }
func (g *GotoNode) Children() []Node { return g.children }
func (g *GotoNode) String() string   { return fmt.Sprintf("Goto %s", g.name) }

type EndNode struct {
	ch       Chan
	children []Node
}

func (e *EndNode) Kind() op   { return EndOp }
func (e *EndNode) Chan() Chan { return e.ch }
func (e *EndNode) Append(n Node) Node {
	e.children = append(e.children, n)
	return n
}
func (e *EndNode) Child(i int) Node { return e.children[i] }
func (e *EndNode) Children() []Node { return e.children }
func (e *EndNode) String() string   { return fmt.Sprintf("End %s", e.ch.Name()) }

type EmptyBodyNode struct {
	children []Node
}

func (e *EmptyBodyNode) Kind() op { return NoOp }
func (e *EmptyBodyNode) Append(node Node) Node {
	e.children = append(e.children, node)
	return node
}
func (e *EmptyBodyNode) Child(i int) Node { return e.children[i] }
func (e *EmptyBodyNode) Children() []Node { return e.children }
func (e *EmptyBodyNode) String() string   { return "(Empty)" }

// NewNewChanNode makes a NewChanNode.
func NewNewChanNode(ch Chan) Node {
	return &NewChanNode{ch: ch, children: []Node{}}
}

// NewSendNode makes a SendNode.
func NewSendNode(sndr Role, dest Chan, typ types.Type) Node {
	return &SendNode{
		sndr:     sndr,
		dest:     dest,
		nondet:   false,
		t:        typ,
		children: []Node{},
	}
}

// NewSelectSendNode makes a SendNode in a select (non-deterministic).
func NewSelectSendNode(sndr Role, dest Chan, typ types.Type) Node {
	return &SendNode{
		sndr:     sndr,
		dest:     dest,
		nondet:   true,
		t:        typ,
		children: []Node{},
	}
}

// NewRecvNode makes a RecvNode.
func NewRecvNode(orig Chan, rcvr Role, typ types.Type) Node {
	return &RecvNode{
		orig:     orig,
		rcvr:     rcvr,
		nondet:   false,
		t:        typ,
		stop:     false,
		children: []Node{},
	}
}

// NewRecvStopNode makes a RecvNode (for STOP messages).
func NewRecvStopNode(orig Chan, rcvr Role, typ types.Type) Node {
	return &RecvNode{
		orig:     orig,
		rcvr:     rcvr,
		nondet:   false,
		t:        typ,
		stop:     true,
		children: []Node{},
	}
}

// NewSelectRecvNode makes a RecvNode in a select (non-deterministic).
func NewSelectRecvNode(orig Chan, rcvr Role, typ types.Type) Node {
	return &RecvNode{
		orig:     orig,
		rcvr:     rcvr,
		nondet:   true,
		t:        typ,
		children: []Node{},
	}
}

// NewLabelNode makes a LabelNode.
func NewLabelNode(name string) Node {
	return &LabelNode{
		name:     name,
		children: []Node{},
	}
}

// NewGotoNode makes a GotoNode.
func NewGotoNode(name string) Node {
	return &GotoNode{
		name:     name,
		children: []Node{},
	}
}

// NewEndNode makse an EndNode.
func NewEndNode(ch Chan) Node {
	return &EndNode{
		ch: ch,
	}
}

// String displays session details.
func (s *Session) String() string {
	str := "# Channels\n"
	for _, ch := range s.Chans {
		str += fmt.Sprintf("%s ", ch.Name())
	}
	str += "\n# Role\n"
	for _, r := range s.Roles {
		str += fmt.Sprintf("%s ", r.Name())
	}
	str += "\n# Session\n"
	for role, session := range s.Types {
		str += fmt.Sprintf("  %s: %s", role.Name(), StringRecursive(session))
		str += "\n"
	}
	return str
}

func StringRecursive(node Node) string {
	str := ""
	if node == nil {
		return str
	}

	str += node.String() + "; "
	switch len(node.Children()) {
	case 0:
	case 1:
		str += StringRecursive(node.Children()[0])
	default:
		str += fmt.Sprintf("children: %d &{", len(node.Children()))
		for i, child := range node.Children() {
			if i > 0 {
				str += ","
			}
			str += StringRecursive(child)
		}
		str += "}"
	}
	return str
}


================================================
FILE: cfsmextract/sesstype/sesstype_test.go
================================================
package sesstype

import (
	"go/token"
	"go/types"
	"testing"

	"github.com/nickng/cfsm"
	"github.com/nickng/dingo-hunter/cfsmextract/utils"
	"golang.org/x/tools/go/ssa"
)

// Tests SendNode creation.
func TestSendNode(t *testing.T) {
	s := CreateSession()
	r := s.GetRole("main")
	c := s.MakeChan(utils.NewDef(utils.EmptyValue{T: nil}), r)
	n := NewSendNode(r, c, nil)
	if n.Kind() != SendOp {
		t.Errorf("Expecting node kind to be %s but got %s\n", SendOp, n.Kind())
	}
	if n.(*SendNode).nondet {
		t.Errorf("Expecting Send to be deterministic by default\n")
	}
	if len(n.Children()) != 0 {
		t.Errorf("Expecting node to have 0 children but got %d\n", len(n.Children()))
	}

	n2 := NewSelectSendNode(r, c, nil)
	if n2.Kind() != SendOp {
		t.Errorf("Expecting node kind to be %s but got %s\n", SendOp, n2.Kind())
	}
	if !n2.(*SendNode).nondet {
		t.Errorf("Expecting Select-Send to be non-deterministic by default\n")
	}
	if len(n2.Children()) != 0 {
		t.Errorf("Expecting node to have 0 children but got %d\n", len(n2.Children()))
	}

	if n2 != n.Append(n2) {
		t.Errorf("Appended node is not same as expected\n")
	}
	if len(n.Children()) != 1 {
		t.Errorf("Expecting node to have 1 children but got %d\n", len(n.Children()))
	}

}

// Tests RecvNode creation.
func TestRecvNode(t *testing.T) {
	s := CreateSession()
	r := s.GetRole("main")
	c := s.MakeChan(utils.NewDef(utils.EmptyValue{T: nil}), r)
	n := NewRecvNode(c, r, nil)
	if n.Kind() != RecvOp {
		t.Errorf("Expecting node kind to be %s but got %s\n", RecvOp, n.Kind())
	}
	if n.(*RecvNode).nondet {
		t.Errorf("Expecting Receive to be deterministic by default\n")
	}
	if len(n.Children()) != 0 {
		t.Errorf("Expecting node to have 0 children but got %d\n", len(n.Children()))
	}

	n2 := NewSelectRecvNode(c, r, nil)
	if n2.Kind() != RecvOp {
		t.Errorf("Expecting node kind to be %s but got %s\n", RecvOp, n2.Kind())
	}
	if !n2.(*RecvNode).nondet {
		t.Errorf("Expecting Select-Recv to be non-deterministic by default\n")
	}
	if len(n2.Children()) != 0 {
		t.Errorf("Expecting node to have 0 children but got %d\n", len(n2.Children()))
	}

	if n2 != n.Append(n2) {
		t.Errorf("Appended node is not same as expected\n")
	}
	if len(n.Children()) != 1 {
		t.Errorf("Expecting node to have 1 children but got %d\n", len(n.Children()))
	}

}

// Tests LabelNode and GotoNode creation.
func TestLabelGotoNode(t *testing.T) {
	l := NewLabelNode("Name")
	if l.Kind() != NoOp {
		t.Errorf("Expecting Goto node kind to be %s but got %s\n", NoOp, l.Kind())
	}
	if len(l.Children()) != 0 {
		t.Errorf("Expecting Label node to have 0 children but got %d\n", len(l.Children()))
	}

	g := NewGotoNode("Name")
	if g.Kind() != NoOp {
		t.Errorf("Expecting Goto node kind to be %s but got %s\n", NoOp, g.Kind())
	}
	if len(g.Children()) != 0 {
		t.Errorf("Expecting Goto node to have 0 children but got %d\n", len(g.Children()))
	}

	if g != l.Append(g) {
		t.Error("Appended node is not same as expected\n")
	}
	if len(l.Children()) != 1 {
		t.Errorf("Expecting Label node to have 1 children but got %d\n", len(l.Children()))
	}
}

// Tests NewChanNode creation.
func TestNewChanNode(t *testing.T) {
	s := CreateSession()
	r := s.GetRole("main")
	c := s.MakeChan(utils.NewDef(utils.EmptyValue{T: nil}), r)
	n := NewNewChanNode(c)
	if n.Kind() != NewChanOp {
		t.Errorf("Expecting node kind to be %s but got %s\n", NewChanOp, n.Kind())
	}
	if len(n.Children()) != 0 {
		t.Errorf("Expecting node to have 0 children but got %d\n", len(n.Children()))
	}
	n2 := NewNewChanNode(c)
	if n2 != n.Append(n2) {
		t.Errorf("Appended node is not same as expected\n")
	}
	if len(n.Children()) != 1 {
		t.Errorf("Expecting node to have 1 children but got %d\n", len(n.Children()))
	}
}

// Tests NewEndNode creation.
func TestEndNode(t *testing.T) {
	s := CreateSession()
	r := s.GetRole("main")
	c := s.MakeChan(utils.NewDef(utils.EmptyValue{T: nil}), r)
	n := NewEndNode(c)
	if n.Kind() != EndOp {
		t.Errorf("Expecting node kind to be %s but got %s\n", EndOp, n.Kind())
	}
	if len(n.Children()) != 0 {
		t.Errorf("Expecting node to have 0 children but got %d\n", len(n.Children()))
	}
	n2 := NewEndNode(c)
	if n2 != n.Append(n2) {
		t.Errorf("Appended node is not same as expected\n")
	}
	if len(n.Children()) != 1 {
		t.Errorf("Expecting node to have 1 children but got %d\n", len(n.Children()))
	}
}

type mockChan struct{}

func (mc mockChan) Name() string                  { return "MockChan" }
func (mc mockChan) String() string                { return "Mock Chan" }
func (mc mockChan) Type() types.Type              { return types.NewChan(types.SendRecv, types.NewStruct(nil, nil)) }
func (mc mockChan) Parent() *ssa.Function         { return nil }
func (mc mockChan) Referrers() *[]ssa.Instruction { return nil }
func (mc mockChan) Pos() token.Pos                { return token.NoPos }

func TestSelfLoop(t *testing.T) {
	s := CreateSession()
	r := s.GetRole("main")
	c := s.MakeChan(utils.NewDef(mockChan{}), r)

	n0 := NewLabelNode("BeforeReceive")
	n1 := NewRecvNode(c, r, types.NewStruct(nil, nil))
	n2 := NewGotoNode("BeforeReceive")
	n0.Append(n1)
	n1.Append(n2)

	ms := NewCFSMs(s)
	m := ms.Sys.NewMachine()
	ms.States[m] = make(map[string]*cfsm.State)
	ms.rootToMachine(r, n0, m)
	if want, got := 1, len(m.States()); want != got {
		t.Errorf("expecting %d states for self-loop but got %d", want, got)
	}
	if want, got := 1, len(m.States()[0].Transitions()); want != got {
		t.Errorf("expecting %d transitions for self-loop but got %d", want, got)
	}
	if from, to := m.States()[0], m.States()[0].Transitions()[0].State(); from != to {
		t.Errorf("expecting self-loop but got %s", m.String())
	}
}


================================================
FILE: cfsmextract/utils/defs.go
================================================
package utils

import (
	"fmt"

	"golang.org/x/tools/go/ssa"
)

var (
	VarVers = make(map[ssa.Value]int)
)

// Variable definitions
type Definition struct {
	Var ssa.Value
	Ver int
}

// NewVarDef creates a new variable definition from an ssa.Value
func NewDef(v ssa.Value) *Definition {
	if v == nil {
		panic("NewVarDef: Cannot create new VarDef with nil")
	}
	if ver, ok := VarVers[v]; ok {
		VarVers[v]++
		return &Definition{
			Var: v,
			Ver: ver + 1,
		}
	}
	VarVers[v] = 0
	return &Definition{
		Var: v,
		Ver: 0,
	}
}

func (vd *Definition) String() string {
	if vd == nil || vd.Var == nil {
		return "Undefined"
	}
	if vd.Var.Parent() != nil {
		return fmt.Sprintf("%s.%s@%d", vd.Var.Parent().String(), vd.Var.Name(), vd.Ver)
	}
	return fmt.Sprintf("???.%s@%d", vd.Var.Name(), vd.Ver)
}


================================================
FILE: cfsmextract/utils/emptyvalue.go
================================================
package utils

import (
	"go/token"
	"go/types"

	"golang.org/x/tools/go/ssa"
)

var (
	_ ssa.Value = (*EmptyValue)(nil) // Make sure it implements ssa.Value.
)

// EmptyValue is a ssa.Value placeholder for values we don't care.
type EmptyValue struct {
	T types.Type
}

func (v EmptyValue) Name() string                  { return "(Nothingness)" }
func (v EmptyValue) String() string                { return "(Empty Value)" }
func (v EmptyValue) Type() types.Type              { return v.T }
func (v EmptyValue) Parent() *ssa.Function         { return nil }
func (v EmptyValue) Referrers() *[]ssa.Instruction { return nil }
func (v EmptyValue) Pos() token.Pos                { return token.NoPos }


================================================
FILE: cfsmextract/utils.go
================================================
package cfsmextract

// From golang.org/x/tools/go/ssa/interp/interp.go

import (
	"fmt"
	"go/token"
	"go/types"

	"golang.org/x/tools/go/ssa"
)

func loc(fr *frame, pos token.Pos) string {
	if fr.fn == nil {
		return "(unknown)"
	}
	if pos == token.NoPos {
		return "(unknown)"
	}
	return fr.fn.Prog.Fset.Position(pos).String()
}

func red(s string) string {
	return fmt.Sprintf("\033[31m%s\033[0m", s)
}

func orange(s string) string {
	return fmt.Sprintf("\033[33m%s\033[0m", s)
}

func green(s string) string {
	return fmt.Sprintf("\033[32m%s\033[0m", s)
}

func cyan(s string) string {
	return fmt.Sprintf("\033[36m%s\033[0m", s)
}

func reg(reg ssa.Value) string {
	if reg == nil {
		return "???.nil"
	}
	if reg.Parent() != nil {
		return fmt.Sprintf("%s.\033[4m%s\033[0m", reg.Parent().String(), reg.Name())
	}
	return fmt.Sprintf("???.\033[4m%s\033[0m", reg.Name())
}

func deref(typ types.Type) types.Type {
	if p, ok := typ.Underlying().(*types.Pointer); ok {
		return p.Elem()
	}
	return typ
}

func derefAll(typ types.Type) types.Type {
	t := typ
	for {
		if p, ok := t.Underlying().(*types.Pointer); ok {
			t = p.Elem()
		} else {
			return t
		}
	}
}


================================================
FILE: cfsmextract/visit.go
================================================
package cfsmextract

import (
	"fmt"
	"go/token"
	"go/types"
	"os"

	"github.com/nickng/dingo-hunter/cfsmextract/sesstype"
	"github.com/nickng/dingo-hunter/cfsmextract/utils"
	"golang.org/x/tools/go/ssa"
)

func visitBlock(blk *ssa.BasicBlock, fr *frame) {
	if len(blk.Preds) > 1 {
		blkLabel := fmt.Sprintf("%s#%d", blk.Parent().String(), blk.Index)

		if _, found := fr.gortn.visited[blk]; found {
			fr.gortn.AddNode(sesstype.NewGotoNode(blkLabel))
			return
		}
		// Make a label for other edges that enter this block
		label := sesstype.NewLabelNode(blkLabel)
		fr.gortn.AddNode(label)
		fr.gortn.visited[blk] = label // XXX visited is initialised by append if lblNode is head of tree
	}

	for _, inst := range blk.Instrs {
		visitInst(inst, fr)
	}
}

// visitFunc is called to traverse a function using given callee frame
// Returns a boolean representing whether or not there are code in the func.
func visitFunc(fn *ssa.Function, callee *frame) bool {
	if fn.Blocks == nil {
		//fmt.Fprintf(os.Stderr, "  # Ignore builtin/external '"+fn.String()+"' with no Blocks\n")
		return false
	}

	visitBlock(fn.Blocks[0], callee)
	return true
}

func visitInst(inst ssa.Instruction, fr *frame) {
	switch inst := inst.(type) {
	case *ssa.MakeChan:
		visitMakeChan(inst, fr)

	case *ssa.Send:
		visitSend(inst, fr)

	case *ssa.UnOp:
		switch inst.Op {
		case token.ARROW:
			visitRecv(inst, fr)
		case token.MUL:
			visitDeref(inst, fr)
		default:
			fmt.Fprintf(os.Stderr, "   # unhandled %s = %s\n", red(inst.Name()), red(inst.String()))
		}

	case *ssa.Call:
		visitCall(inst, fr)

	case *ssa.Extract:
		visitExtract(inst, fr)

	case *ssa.Go:
		fr.callGo(inst)

	case *ssa.Return:
		fr.retvals = visitReturn(inst, fr)

	case *ssa.Store:
		visitStore(inst, fr)

	case *ssa.Alloc:
		visitAlloc(inst, fr)

	case *ssa.MakeClosure:
		visitMakeClosure(inst, fr)

	case *ssa.Select:
		visitSelect(inst, fr)

	case *ssa.ChangeType:
		visitChangeType(inst, fr)

	case *ssa.ChangeInterface:
		visitChangeInterface(inst, fr)

	case *ssa.If:
		visitIf(inst, fr)

	case *ssa.Jump:
		visitJump(inst, fr)

	case *ssa.BinOp:
		visitBinOp(inst, fr)

	case *ssa.Slice:
		visitSlice(inst, fr)

	case *ssa.MakeSlice:
		visitMakeSlice(inst, fr)

	case *ssa.FieldAddr:
		visitFieldAddr(inst, fr)

	case *ssa.Field:
		visitField(inst, fr)

	case *ssa.IndexAddr:
		visitIndexAddr(inst, fr)

	case *ssa.Index:
		visitIndex(inst, fr)

	case *ssa.Defer:
		visitDefer(inst, fr)

	case *ssa.RunDefers:
		visitRunDefers(inst, fr)

	case *ssa.Phi:
		visitPhi(inst, fr)

	case *ssa.TypeAssert:
		visitTypeAssert(inst, fr)

	case *ssa.MakeInterface:
		visitMakeInterface(inst, fr)

	case *ssa.DebugRef:

	default:
		// Everything else not handled yet
		if v, ok := inst.(ssa.Value); ok {
			fmt.Fprintf(os.Stderr, "   # unhandled %s = %s\n", red(v.Name()), red(v.String()))
		} else {
			fmt.Fprintf(os.Stderr, "   # unhandled %s\n", red(inst.String()))
		}
	}
}

func visitExtract(e *ssa.Extract, fr *frame) {
	if recvCh, ok := fr.recvok[e.Tuple]; ok && e.Index == 1 { // 1 = ok (bool)
		fmt.Fprintf(os.Stderr, "  EXTRACT for %s\n", recvCh.Name())
		//fr.locals[e] = e
		fr.env.recvTest[e] = recvCh
		return
	}
	if tpl, ok := fr.tuples[e.Tuple]; ok {
		fmt.Fprintf(os.Stderr, "   %s = extract %s[#%d] == %s\n", reg(e), e.Tuple.Name(), e.Index, tpl[e.Index].String())
		fr.locals[e] = tpl[e.Index]
	} else {
		// Check if we are extracting select index
		if _, ok := fr.env.selNode[e.Tuple]; ok && e.Index == 0 {
			fmt.Fprintf(os.Stderr, "   | %s = select %s index\n", e.Name(), e.Tuple.Name())
			fr.env.selIdx[e] = e.Tuple
			return
		}
		// Check if value is an external tuple (return value)
		if extType, isExtern := fr.env.extern[e.Tuple]; isExtern {
			if extTpl, isTuple := extType.(*types.Tuple); isTuple {
				if extTpl.Len() < e.Index {
					panic(fmt.Sprintf("Extract: Cannot extract from tuple %s\n", e.Tuple.Name()))
				}
				// if extracted value is a chan create a new channel for it
				if _, ok := extTpl.At(e.Index).Type().(*types.Chan); ok {
					panic("Extract: Undefined channel")
				}
			}
			if e.Index < len(tpl) {
				fmt.Fprintf(os.Stderr, "  extract %s[#%d] == %s\n", e.Tuple.Name(), e.Index, tpl[e.Index].String())
			} else {
				fmt.Fprintf(os.Stderr, "  extract %s[#%d/%d]\n", e.Tuple.Name(), e.Index, len(tpl))
			}
		} else {
			fmt.Fprintf(os.Stderr, "   # %s = %s of type %s\n", e.Name(), red(e.String()), e.Type().String())
			switch derefAll(e.Type()).Underlying().(type) {
			case *types.Array:
				vd := utils.NewDef(e)
				fr.locals[e] = vd
				fr.arrays[vd] = make(Elems)
				fmt.Fprintf(os.Stderr, "     ^ local array (used as definition)\n")
			case *types.Struct:
				vd := utils.NewDef(e)
				fr.locals[e] = vd
				fr.structs[vd] = make(Fields)
				fmt.Fprintf(os.Stderr, "     ^ local struct (used as definition)\n")
			}
		}
	}
}

func visitMakeClosure(inst *ssa.MakeClosure, fr *frame) {
	fr.env.closures[inst] = make([]*utils.Definition, 0)
	for _, binding := range inst.Bindings {
		fr.env.closures[inst] = append(fr.env.closures[inst], fr.locals[binding])
	}
}

// visitAlloc is for variable allocation (usually by 'new')
// Everything allocated here are pointers
func visitAlloc(inst *ssa.Alloc, fr *frame) {
	locn := loc(fr, inst.Pos())
	allocType := inst.Type().(*types.Pointer).Elem()
	if allocType == nil {
		panic("Alloc: Cannot Alloc for non-pointer type")
	}
	var val ssa.Value = inst

	switch t := allocType.Underlying().(type) {
	case *types.Array:
		vd := utils.NewDef(val)
		fr.locals[val] = vd
		if inst.Heap {
			fr.env.arrays[vd] = make(Elems)
			fmt.Fprintf(os.Stderr, "   %s = Alloc (array@heap) of type %s (%d elems) at %s\n", cyan(reg(inst)), inst.Type().String(), t.Len(), locn)
		} else {
			fr.arrays[vd] = make(Elems)
			fmt.Fprintf(os.Stderr, "   %s = Alloc (array@local) of type %s (%d elems) at %s\n", cyan(reg(inst)), inst.Type().String(), t.Len(), locn)
		}

	case *types.Chan:
		// VD will be created in MakeChan so no need to allocate here.
		fmt.Fprintf(os.Stderr, "   %s = Alloc (chan) of type %s at %s\n", cyan(reg(inst)), inst.Type().String(), locn)

	case *types.Struct:
		vd := utils.NewDef(val)
		fr.locals[val] = vd
		if inst.Heap {
			fr.env.structs[vd] = make(Fields, t.NumFields())
			fmt.Fprintf(os.Stderr, "   %s = Alloc (struct@heap) of type %s (%d fields) at %s\n", cyan(reg(inst)), inst.Type().String(), t.NumFields(), locn)
		} else {
			fr.structs[vd] = make(Fields, t.NumFields())
			fmt.Fprintf(os.Stderr, "   %s = Alloc (struct@local) of type %s (%d fields) at %s\n", cyan(reg(inst)), inst.Type().String(), t.NumFields(), locn)
		}

	default:
		fmt.Fprintf(os.Stderr, "   # %s = "+red("Alloc %s")+" of type %s\n", inst.Name(), inst.String(), t.String())
	}
}

func visitDeref(inst *ssa.UnOp, fr *frame) {
	ptr := inst.X
	val := inst

	if _, ok := ptr.(*ssa.Global); ok {
		fr.locals[ptr] = fr.env.globals[ptr]
		fmt.Fprintf(os.Stderr, "   %s = *%s (global) of type %s\n", cyan(reg(val)), ptr.Name(), ptr.Type().String())
		fmt.Fprintf(os.Stderr, "    ^ i.e. %s\n", fr.locals[ptr].String())

		switch deref(fr.locals[ptr].Var.Type()).(type) {
		case *types.Array, *types.Slice:
			if _, ok := fr.env.arrays[fr.env.globals[ptr]]; !ok {
				fr.env.arrays[fr.env.globals[ptr]] = make(Elems)
			}

		case *types.Struct:
			if _, ok := fr.env.structs[fr.env.globals[ptr]]; !ok {
				fr.env.structs[fr.env.globals[ptr]] = make(Fields)
			}
		}
	}

	switch vd, kind := fr.get(ptr); kind {
	case Array, LocalArray:
		fr.locals[val] = vd
		fmt.Fprintf(os.Stderr, "   %s = *%s (array)\n", cyan(reg(val)), ptr.Name())

	case Struct, LocalStruct:
		fr.locals[val] = vd
		fmt.Fprintf(os.Stderr, "   %s = *%s (struct)\n", cyan(reg(val)), ptr.Name())

	case Chan:
		fr.locals[val] = vd
		fmt.Fprintf(os.Stderr, "   %s = *%s (previously initalised Chan)\n", cyan(reg(val)), ptr.Name())

	case Nothing:
		fmt.Fprintf(os.Stderr, "   # %s = *%s (not found)\n", red(inst.String()), red(inst.X.String()))
		if _, ok := val.Type().Underlying().(*types.Chan); ok {
			fmt.Fprintf(os.Stderr, "     ^ channel (not allocated, must be initialised by MakeChan)")
		}

	default:
		fmt.Fprintf(os.Stderr, "   # %s = *%s/%s (not found, type=%s)\n", red(inst.String()), red(inst.X.String()), reg(inst.X), inst.Type().String())
	}
}

func visitSelect(s *ssa.Select, fr *frame) {
	if fr.gortn.leaf == nil {
		panic("Select: Session head Node cannot be nil")
	}

	fr.env.selNode[s] = struct {
		parent   *sesstype.Node
		blocking bool
	}{
		fr.gortn.leaf,
		s.Blocking,
	}
	for _, state := range s.States {
		locn := loc(fr, state.Chan.Pos())
		switch vd, kind := fr.get(state.Chan); kind {
		case Chan:
			ch := fr.env.chans[vd]
			fmt.Fprintf(os.Stderr, "   select "+orange("%s")+" (%d states)\n", vd.String(), len(s.States))
			switch state.Dir {
			case types.SendOnly:
				fr.gortn.leaf = fr.env.selNode[s].parent
				fr.gortn.AddNode(sesstype.NewSelectSendNode(fr.gortn.role, *ch, state.Chan.Type()))
				fmt.Fprintf(os.Stderr, "    %s\n", orange((*fr.gortn.leaf).String()))

			case types.RecvOnly:
				fr.gortn.leaf = fr.env.selNode[s].parent
				fr.gortn.AddNode(sesstype.NewSelectRecvNode(*ch, fr.gortn.role, state.Chan.Type()))
				fmt.Fprintf(os.Stderr, "    %s\n", orange((*fr.gortn.leaf).String()))

			default:
				panic("Select: Cannot handle with SendRecv channels")
			}

		case Nothing:
			fr.printCallStack()
			panic(fmt.Sprintf("Select: Channel %s at %s is undefined", reg(state.Chan), locn))

		default:
			fr.printCallStack()
			panic(fmt.Sprintf("Select: Channel %s at %s is of wrong kind", reg(state.Chan), locn))
		}
	}
	if !s.Blocking { // Default state exists
		fr.gortn.leaf = fr.env.selNode[s].parent
		fr.gortn.AddNode(&sesstype.EmptyBodyNode{})
		fmt.Fprintf(os.Stderr, "    Default: %s\n", orange((*fr.gortn.leaf).String()))
	}
}

func visitReturn(ret *ssa.Return, fr *frame) []*utils.Definition {
	var vds []*utils.Definition
	for _, result := range ret.Results {
		vds = append(vds, fr.locals[result])
	}
	return vds
}

// Handles function call.
// Wrapper for calling visitFunc and performing argument translation.
func visitCall(c *ssa.Call, caller *frame) {
	caller.call(c)
}

func visitIf(inst *ssa.If, fr *frame) {
	if len(inst.Block().Succs) != 2 {
		panic("If: Cannot handle If with more or less than 2 successor blocks!")
	}

	ifparent := fr.gortn.leaf
	if ifparent == nil {
		panic("If: Parent is nil")
	}

	if ch, isRecvTest := fr.env.recvTest[inst.Cond]; isRecvTest {
		fmt.Fprintf(os.Stderr, "  @ Switch to recvtest true\n")
		fr.gortn.leaf = ifparent
		fr.gortn.AddNode(sesstype.NewRecvNode(*ch, fr.gortn.role, ch.Type()))
		fmt.Fprintf(os.Stderr, "  %s\n", orange((*fr.gortn.leaf).String()))
		visitBlock(inst.Block().Succs[0], fr)

		fmt.Fprintf(os.Stderr, "  @ Switch to recvtest false\n")
		fr.gortn.leaf = ifparent
		fr.gortn.AddNode(sesstype.NewRecvStopNode(*ch, fr.gortn.role, ch.Type()))
		fmt.Fprintf(os.Stderr, "  %s\n", orange((*fr.gortn.leaf).String()))
		visitBlock(inst.Block().Succs[1], fr)
	} else if selTest, isSelTest := fr.env.selTest[inst.Cond]; isSelTest {
		// Check if this is a select-test-jump, if so handle separately.
		fmt.Fprintf(os.Stderr, "  @ Switch to select branch #%d\n", selTest.idx)
		if selParent, ok := fr.env.selNode[selTest.tpl]; ok {
			fr.gortn.leaf = ifparent
			*fr.gortn.leaf = (*selParent.parent).Child(selTest.idx)
			visitBlock(inst.Block().Succs[0], fr)

			if !selParent.blocking && len((*selParent.parent).Children()) > selTest.idx+1 {
				*fr.gortn.leaf = (*selParent.parent).Child(selTest.idx + 1)
			}
			visitBlock(inst.Block().Succs[1], fr)
		} else {
			panic("Select without corresponding sesstype.Node")
		}
	} else {
		fr.env.ifparent.Push(*fr.gortn.leaf)

		parent := fr.env.ifparent.Top()
		fr.gortn.leaf = &parent
		fr.gortn.AddNode(&sesstype.EmptyBodyNode{})
		visitBlock(inst.Block().Succs[0], fr)

		parent = fr.env.ifparent.Top()
		fr.gortn.leaf = &parent
		fr.gortn.AddNode(&sesstype.EmptyBodyNode{})
		visitBlock(inst.Block().Succs[1], fr)

		fr.env.ifparent.Pop()
	}
	// This is end of the block so no continuation
}

func visitMakeChan(inst *ssa.MakeChan, caller *frame) {
	locn := loc(caller, inst.Pos())
	role := caller.gortn.role

	vd := utils.NewDef(inst) // Unique identifier for inst
	ch := caller.env.session.MakeChan(vd, role)

	caller.env.chans[vd] = &ch
	caller.gortn.AddNode(sesstype.NewNewChanNode(ch))
	caller.locals[inst] = vd
	fmt.Fprintf(os.Stderr, "   New channel %s { type: %s } by %s at %s\n", green(ch.Name()), ch.Type(), vd.String(), locn)
	fmt.Fprintf(os.Stderr, "               ^ in role %s\n", role.Name())
}

func visitSend(send *ssa.Send, fr *frame) {
	locn := loc(fr, send.Chan.Pos())
	if vd, kind := fr.get(send.Chan); kind == Chan {
		ch := fr.env.chans[vd]
		fr.gortn.AddNode(sesstype.NewSendNode(fr.gortn.role, *ch, send.Chan.Type()))
		fmt.Fprintf(os.Stderr, "  %s\n", orange((*fr.gortn.leaf).String()))
	} else if kind == Nothing {
		fr.locals[send.Chan] = utils.NewDef(send.Chan)
		ch := fr.env.session.MakeExtChan(fr.locals[send.Chan], fr.gortn.role)
		fr.env.chans[fr.locals[send.Chan]] = &ch
		fr.gortn.AddNode(sesstype.NewSendNode(fr.gortn.role, ch, send.Chan.Type()))
		fmt.Fprintf(os.Stderr, "  %s\n", orange((*fr.gortn.leaf).String()))
		fmt.Fprintf(os.Stderr, "   ^ Send: Channel %s at %s is external\n", reg(send.Chan), locn)
	} else {
		fr.printCallStack()
		panic(fmt.Sprintf("Send: Channel %s at %s is of wrong kind", reg(send.Chan), locn))
	}
}

func visitRecv(recv *ssa.UnOp, fr *frame) {
	locn := loc(fr, recv.X.Pos())
	if vd, kind := fr.get(recv.X); kind == Chan {
		ch := fr.env.chans[vd]
		if recv.CommaOk {
			// ReceiveOK test
			fr.recvok[recv] = ch
			// TODO(nickng) technically this should do receive (both branches)
		} else {
			// Normal receive
			fr.gortn.AddNode(sesstype.NewRecvNode(*ch, fr.gortn.role, recv.X.Type()))
			fmt.Fprintf(os.Stderr, "  %s\n", orange((*fr.gortn.leaf).String()))
		}
	} else if kind == Nothing {
		fr.locals[recv.X] = utils.NewDef(recv.X)
		ch := fr.env.session.MakeExtChan(fr.locals[recv.X], fr.gortn.role)
		fr.env.chans[fr.locals[recv.X]] = &ch
		fr.gortn.AddNode(sesstype.NewRecvNode(ch, fr.gortn.role, recv.X.Type()))
		fmt.Fprintf(os.Stderr, "  %s\n", orange((*fr.gortn.leaf).String()))
		fmt.Fprintf(os.Stderr, "   ^ Recv: Channel %s at %s is external\n", reg(recv.X), locn)
	} else {
		fr.printCallStack()
		panic(fmt.Sprintf("Recv: Channel %s at %s is of wrong kind", reg(recv.X), locn))
	}
}

// visitClose for the close() builtin primitive.
func visitClose(ch sesstype.Chan, fr *frame) {
	fr.gortn.AddNode(sesstype.NewEndNode(ch))
}

func visitJump(inst *ssa.Jump, fr *frame) {
	//fmt.Fprintf(os.Stderr, " -jump-> Block %d\n", inst.Block().Succs[0].Index)
	if len(inst.Block().Succs) != 1 {
		panic("Cannot Jump with multiple successors!")
	}
	visitBlock(inst.Block().Succs[0], fr)
}

func visitStore(inst *ssa.Store, fr *frame) {
	source := inst.Val
	dstPtr := inst.Addr // from Alloc or field/elem access

	if _, ok := dstPtr.(*ssa.Global); ok {
		vdOld, _ := fr.env.globals[dstPtr]
		switch vd, kind := fr.get(source); kind {
		case Array:
			fr.env.globals[dstPtr] = vd
			fr.updateDefs(vdOld, vd)
			fmt.Fprintf(os.Stderr, "   # store (global) *%s = %s of type %s\n", dstPtr.String(), source.Name(), source.Type().String())

		case Struct:
			fr.env.globals[dstPtr] = vd
			fr.updateDefs(vdOld, vd)
			fmt.Fprintf(os.Stderr, "   # store (global) *%s = %s of type %s\n", reg(dstPtr), reg(source), source.Type().String())

		default:
			fmt.Fprintf(os.Stderr, "   # store (global) *%s = %s of type %s\n", red(reg(dstPtr)), reg(source), source.Type().String())
		}
	} else {
		vdOld, _ := fr.get(dstPtr)
		switch vd, kind := fr.get(source); kind {
		case Array:
			// Pre: fr.locals[source] points to vd
			// Pre: fr.locals[dstPtr] points to (empty/outdated) vdOld
			// Post: fr.locals[source] unchanged
			// Post: fr.locals[dstPtr] points to vd
			fr.locals[dstPtr] = vd   // was vdOld
			fr.updateDefs(vdOld, vd) // Update all references to vdOld to vd
			fmt.Fprintf(os.Stderr, "   # store array *%s = %s of type %s\n", cyan(reg(dstPtr)), reg(source), source.Type().String())

		case LocalArray:
			fr.locals[dstPtr] = vd
			fr.updateDefs(vdOld, vd)
			fmt.Fprintf(os.Stderr, "   store larray *%s = %s of type %s\n", cyan(reg(dstPtr)), reg(source), source.Type().String())

		case Chan:
			fr.locals[dstPtr] = vd
			fr.updateDefs(vdOld, vd)
			fmt.Fprintf(os.Stderr, "   store chan *%s = %s of type %s\n", cyan(reg(dstPtr)), reg(source), source.Type().String())

		case Struct:
			fr.locals[dstPtr] = vd
			fr.updateDefs(vdOld, vd)
			fmt.Fprintf(os.Stderr, "   store struct *%s = %s of type %s\n", cyan(reg(dstPtr)), reg(source), source.Type().String())

		case LocalStruct:
			fr.locals[dstPtr] = vd
			fr.updateDefs(vdOld, vd)
			fmt.Fprintf(os.Stderr, "   store lstruct *%s = %s of type %s\n", cyan(reg(dstPtr)), reg(source), source.Type().String())

		case Untracked:
			fr.locals[dstPtr] = vd
			fmt.Fprintf(os.Stderr, "   store update *%s = %s of type %s\n", cyan(reg(dstPtr)), reg(source), source.Type().String())

		case Nothing:
			fmt.Fprintf(os.Stderr, "   # store *%s = %s of type %s\n", red(reg(dstPtr)), reg(source), source.Type().String())

		default:
			fr.locals[dstPtr] = vd
			fmt.Fprintf(os.Stderr, "   store *%s = %s of type %s\n", cyan(reg(dstPtr)), reg(source), source.Type().String())
		}

	}
}

func visitChangeType(inst *ssa.ChangeType, fr *frame) {
	switch vd, kind := fr.get(inst.X); kind {
	case Chan:
		fr.locals[inst] = vd // ChangeType from <-chan and chan<-
		ch := fr.env.chans[vd]
		fmt.Fprintf(os.Stderr, "   & changetype from %s to %s (channel %s)\n", green(reg(inst.X)), reg(inst), ch.Name())
		fmt.Fprintf(os.Stderr, "                      ^ origin\n")

	case Nothing:
		fmt.Fprintf(os.Stderr, "   # changetype %s = %s %s\n", inst.Name(), inst.X.Name(), inst.String())
		fmt.Fprintf(os.Stderr, "          ^ unknown kind\n")

	default:
		fr.locals[inst] = vd
		fmt.Fprintf(os.Stderr, "   # changetype %s = %s\n", red(inst.Name()), inst.String())
	}
}

func visitChangeInterface(inst *ssa.ChangeInterface, fr *frame) {
	fr.locals[inst] = fr.locals[inst.X]
	fmt.Fprintf(os.Stderr, "   # changeinterface %s = %s\n", reg(inst), inst.String())
}

func visitBinOp(inst *ssa.BinOp, fr *frame) {
	switch inst.Op {
	case token.EQL:
		if selTuple, isSelTuple := fr.env.selIdx[inst.X]; isSelTuple {
			branchID := int(inst.Y.(*ssa.Const).Int64())
			fr.env.selTest[inst] = struct {
				idx int
				tpl ssa.Value
			}{
				branchID, selTuple,
			}
		} else {
			fmt.Fprintf(os.Stderr, "   # %s = "+red("%s")+"\n", inst.Name(), inst.String())
		}
	default:
		fmt.Fprintf(os.Stderr, "   # %s = "+red("%s")+"\n", inst.Name(), inst.String())
	}
}

func visitMakeInterface(inst *ssa.MakeInterface, fr *frame) {
	switch vd, kind := fr.get(inst.X); kind {
	case Struct, LocalStruct:
		fmt.Fprintf(os.Stderr, "   %s <-(struct/iface)- %s %s = %s\n", cyan(reg(inst)), reg(inst.X), inst.String(), vd.String())
		fr.locals[inst] = vd

	case Array, LocalArray:
		fmt.Fprintf(os.Stderr, "   %s <-(array/iface)- %s %s = %s\n", cyan(reg(inst)), reg(inst.X), inst.String(), vd.String())
		fr.locals[inst] = vd

	default:
		fmt.Fprintf(os.Stderr, "   # %s <- %s\n", red(reg(inst)), inst.String())
	}
}

func visitSlice(inst *ssa.Slice, fr *frame) {
	fr.env.arrays[utils.NewDef(inst)] = make(Elems)
}

func visitMakeSlice(inst *ssa.MakeSlice, fr *frame) {
	fr.env.arrays[utils.NewDef(inst)] = make(Elems)
}

func visitFieldAddr(inst *ssa.FieldAddr, fr *frame) {
	field := inst
	struc := inst.X
	index := inst.Field

	if stype, ok := deref(struc.Type()).Underlying().(*types.Struct); ok {
		switch vd, kind := fr.get(struc); kind {
		case Struct:
			fmt.Fprintf(os.Stderr, "   %s = %s(=%s)->[%d] of type %s\n", cyan(reg(field)), struc.Name(), vd.String(), index, field.Type().String())
			if fr.env.structs[vd][index] == nil { // First use
				vdField := utils.NewDef(field)
				fr.env.structs[vd][index] = vdField
				fmt.Fprintf(os.Stderr, "     ^ accessed for the first time: use %s as field definition\n", field.Name())
				// If field is struct
				if fieldType, ok := deref(field.Type()).Underlying().(*types.Struct); ok {
					fr.env.structs[vdField] = make(Fields, fieldType.NumFields())
					fmt.Fprintf(os.Stderr, "     ^ field %s is a struct (allocating)\n", field.Name())
				}
			} else if fr.env.structs[vd][index].Var != field { // Previously defined
				fmt.Fprintf(os.Stderr, "     ^ field %s previously defined as %s\n", field.Name(), reg(fr.env.structs[vd][index].Var))
			} // else Accessed before (and unchanged)
			fr.locals[field] = fr.env.structs[vd][index]

		case LocalStruct:
			fmt.Fprintf(os.Stderr, "   %s = %s(=%s)->[%d] (local) of type %s\n", cyan(reg(field)), struc.Name(), vd.String(), index, field.Type().String())
			if fr.structs[vd][index] == nil { // First use
				vdField := utils.NewDef(field)
				fr.structs[vd][index] = vdField
				fmt.Fprintf(os.Stderr, "     ^ accessed for the first time: use %s as field definition\n", field.Name())
				// If field is struct
				if fieldType, ok := deref(field.Type()).Underlying().(*types.Struct); ok {
					fr.structs[vdField] = make(Fields, fieldType.NumFields())
					fmt.Fprintf(os.Stderr, "     ^ field %s is a struct (allocating locally)\n", field.Name())
				}
			} else if fr.structs[vd][index].Var != field { // Previously defined
				fmt.Fprintf(os.Stderr, "     ^ field %s previously defined as %s\n", field.Name(), reg(fr.structs[vd][index].Var))
			} // else Accessed before (and unchanged)
			fr.locals[field] = fr.structs[vd][index]

		case Nothing, Untracked:
			// Nothing: Very likely external struct.
			// Untracked: likely branches of return values (e.g. returning nil)
			fmt.Fprintf(os.Stderr, "   %s = %s(=%s)->[%d] (external) of type %s\n", cyan(reg(field)), inst.X.Name(), vd.String(), index, field.Type().String())
			vd := utils.NewDef(struc) // New external struct
			fr.locals[struc] = vd
			fr.env.structs[vd] = make(Fields, stype.NumFields())
			vdField := utils.NewDef(field) // New external field
			fr.env.structs[vd][index] = vdField
			fr.locals[field] = vdField
			fmt.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())
			// If field is struct
			if fieldType, ok := deref(field.Type()).Underlying().(*types.Struct); ok {
				fr.env.structs[vdField] = make(Fields, fieldType.NumFields())
				fmt.Fprintf(os.Stderr, "     ^ field %s previously defined as %s\n", field.Name(), reg(fr.env.structs[vd][index].Var))
			}

		default:
			panic(fmt.Sprintf("FieldAddr: Cannot access non-struct %s %T %d", reg(struc), deref(struc.Type()).Underlying(), kind))
		}
	} else {
		panic(fmt.Sprintf("FieldAddr: Cannot access field - %s not a struct\n", reg(struc)))
	}
}

func visitField(inst *ssa.Field, fr *frame) {
	field := inst
	struc := inst.X
	index := inst.Field

	if stype, ok := struc.Type().Underlying().(*types.Struct); ok {
		switch vd, kind := fr.get(struc); kind {
		case Struct:
			fmt.Fprintf(os.Stderr, "   %s = %s(=%s).[%d] of type %s\n", cyan(reg(field)), struc.Name(), vd.String(), index, field.Type().String())
			if fr.env.structs[vd][index] == nil { // First use
				vdField := utils.NewDef(field)
				fr.env.structs[vd][index] = vdField
				fmt.Fprintf(os.Stderr, "     ^ accessed for the first time: use %s as field definition\n", field.Name())
				// If field is struct
				if fieldType, ok := field.Type().Underlying().(*types.Struct); ok {
					fr.env.structs[vdField] = make(Fields, fieldType.NumFields())
					fmt.Fprintf(os.Stderr, "     ^ field %s is a struct (allocating)\n", field.Name())
				}
			} else if fr.env.structs[vd][index].Var != field { // Previously defined
				fmt.Fprintf(os.Stderr, "     ^ field %s previously defined as %s\n", field.Name(), reg(fr.env.structs[vd][index].Var))
			} // else Accessed before (and unchanged)
			fr.locals[field] = fr.env.structs[vd][index]

		case LocalStruct:
			fmt.Fprintf(os.Stderr, "   %s = %s(=%s).[%d] (local) of type %s\n", cyan(reg(field)), struc.Name(), vd.String(), index, field.Type().String())
			if fr.structs[vd][index] == nil { // First use
				vdField := utils.NewDef(field)
				fr.structs[vd][index] = vdField
				fmt.Fprintf(os.Stderr, "     ^ accessed for the first time: use %s as field definition\n", field.Name())
				// If field is struct
				if fieldType, ok := field.Type().Underlying().(*types.Struct); ok {
					fr.structs[vdField] = make(Fields, fieldType.NumFields())
					fmt.Fprintf(os.Stderr, "     ^ field %s is a struct (allocating locally)\n", field.Name())
				}
			} else if fr.structs[vd][index].Var != field { // Previously defined
				fmt.Fprintf(os.Stderr, "     ^ field %s previously defined as %s\n", field.Name(), reg(fr.structs[vd][index].Var))
			} // else Accessed before (and unchanged)
			fr.locals[field] = fr.structs[vd][index]

		case Nothing, Untracked:
			// Nothing: Very likely external struct.
			// Untracked: likely branches of return values (e.g. returning nil)
			fmt.Fprintf(os.Stderr, "   %s = %s(=%s).[%d] (external) of type %s\n", cyan(reg(field)), inst.X.Name(), vd.String(), index, field.Type().String())
			vd := utils.NewDef(struc) // New external struct
			fr.locals[struc] = vd
			fr.env.structs[vd] = make(Fields, stype.NumFields())
			vdField := utils.NewDef(field) // New external field
			fr.env.structs[vd][index] = vdField
			fr.locals[field] = vdField
			fmt.Fprintf(os.Stderr, "     ^ accessed for the first time: use %s as field definition of type %s\n", field.Name(), inst.Type().Underlying().String())
			// If field is struct
			if fieldType, ok := field.Type().Underlying().(*types.Struct); ok {
				fr.env.structs[vdField] = make(Fields, fieldType.NumFields())
				fmt.Fprintf(os.Stderr, "     ^ field %s previously defined as %s\n", field.Name(), reg(fr.env.structs[vd][index].Var))
			}

		default:
			panic(fmt.Sprintf("Field: Cannot access non-struct %s %T %d", reg(struc), struc.Type(), kind))
		}
	} else {
		panic(fmt.Sprintf("Field: Cannot access field - %s not a struct\n", reg(struc)))
	}
}

func visitIndexAddr(inst *ssa.IndexAddr, fr *frame) {
	elem := inst
	array := inst.X
	index := inst.Index
	_, isArray := deref(array.Type()).Underlying().(*types.Array)
	_, isSlice := deref(array.Type()).Underlying().(*types.Slice)

	if isArray || isSlice {
		switch vd, kind := fr.get(array); kind {
		case Array:
			fmt.Fprintf(os.Stderr, "   %s = &%s(=%s)[%d] of type %s\n", cyan(reg(elem)), array.Name(), vd.String(), index, elem.Type().String())
			if fr.env.arrays[vd][index] == nil { // First use
				vdelem := utils.NewDef(elem)
				fr.env.arrays[vd][index] = vdelem
				fmt.Fprintf(os.Stderr, "     ^ accessed for the first time: use %s as elem definition\n", elem.Name())
			} else if fr.env.arrays[vd][index].Var != elem { // Previously defined
				fmt.Fprintf(os.Stderr, "     ^ elem %s previously defined as %s\n", elem.Name(), reg(fr.env.arrays[vd][index].Var))
			} // else Accessed before (and unchanged)
			fr.locals[elem] = fr.env.arrays[vd][index]

		case LocalArray:
			fmt.Fprintf(os.Stderr, "   %s = &%s(=%s)[%d] (local) of type %s\n", cyan(reg(elem)), array.Name(), vd.String(), index, elem.Type().String())
			if fr.arrays[vd][index] == nil { // First use
				vdElem := utils.NewDef(elem)
				fr.arrays[vd][index] = vdElem
				fmt.Fprintf(os.Stderr, "     ^ accessed for the first time: use %s as elem definition\n", elem.Name())
			} else if fr.arrays[vd][index].Var != elem { // Previously defined
				fmt.Fprintf(os.Stderr, "     ^ elem %s previously defined as %s\n", elem.Name(), reg(fr.arrays[vd][index].Var))
			} // else Accessed before (and unchanged)
			fr.locals[elem] = fr.arrays[vd][index]

		case Nothing, Untracked:
			// Nothing: Very likely external struct.
			// Untracked: likely branches of return values (e.g. returning nil)
			fmt.Fprintf(os.Stderr, "   %s = &%s(=%s)[%d] (external) of type %s\n", cyan(reg(elem)), inst.X.Name(), vd.String(), index, elem.Type().String())
			vd := utils.NewDef(array) // New external array
			fr.locals[array] = vd
			fr.env.arrays[vd] = make(Elems)
			vdElem := utils.NewDef(elem) // New external elem
			fr.env.arrays[vd][index] = vdElem
			fr.locals[elem] = vdElem
			fmt.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())

		default:
			panic(fmt.Sprintf("IndexAddr: Cannot access non-array %s", reg(array)))
		}
	} else {
		panic(fmt.Sprintf("IndexAddr: Cannot access field - %s not an array", reg(array)))
	}
}

func visitIndex(inst *ssa.Index, fr *frame) {
	elem := inst
	array := inst.X
	index := inst.Index
	_, isArray := array.Type().Underlying().(*types.Array)
	_, isSlice := array.Type().Underlying().(*types.Slice)

	if isArray || isSlice {
		switch vd, kind := fr.get(array); kind {
		case Array:
			fmt.Fprintf(os.Stderr, "   %s = %s(=%s)[%d] of type %s\n", cyan(reg(elem)), array.Name(), vd.String(), index, elem.Type().String())
			if fr.env.arrays[vd][index] == nil { // First use
				vdelem := utils.NewDef(elem)
				fr.env.arrays[vd][index] = vdelem
				fmt.Fprintf(os.Stderr, "     ^ accessed for the first time: use %s as elem definition\n", elem.Name())
			} else if fr.env.arrays[vd][index].Var != elem { // Previously defined
				fmt.Fprintf(os.Stderr, "     ^ elem %s previously defined as %s\n", elem.Name(), reg(fr.env.arrays[vd][index].Var))
			} // else Accessed before (and unchanged)
			fr.locals[elem] = fr.env.arrays[vd][index]

		case LocalArray:
			fmt.Fprintf(os.Stderr, "   %s = %s(=%s)[%d] (local) of type %s\n", cyan(reg(elem)), array.Name(), vd.String(), index, elem.Type().String())
			if fr.arrays[vd][index] == nil { // First use
				vdElem := utils.NewDef(elem)
				fr.arrays[vd][index] = vdElem
				fmt.Fprintf(os.Stderr, "     ^ accessed for the first time: use %s as elem definition\n", elem.Name())
			} else if fr.arrays[vd][index].Var != elem { // Previously defined
				fmt.Fprintf(os.Stderr, "     ^ elem %s previously defined as %s\n", elem.Name(), reg(fr.arrays[vd][index].Var))
			} // else Accessed before (and unchanged)
			fr.locals[elem] = fr.arrays[vd][index]

		case Nothing, Untracked:
			// Nothing: Very likely external struct.
			// Untracked: likely branches of return values (e.g. returning nil)
			fmt.Fprintf(os.Stderr, "   %s = %s(=%s)[%d] (external) of type %s\n", cyan(reg(elem)), inst.X.Name(), vd.String(), index, elem.Type().String())
			vd := utils.NewDef(array) // New external array
			fr.locals[array] = vd
			fr.env.arrays[vd] = make(Elems)
			vdElem := utils.NewDef(elem) // New external elem
			fr.env.arrays[vd][index] = vdElem
			fr.locals[elem] = vdElem
			fmt.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())

		default:
			panic(fmt.Sprintf("Index: Cannot access non-array %s", reg(array)))
		}
	} else {
		panic(fmt.Sprintf("Index: Cannot access element - %s not an array", reg(array)))
	}
}

func visitDefer(inst *ssa.Defer, fr *frame) {
	fr.defers = append(fr.defers, inst)
}

func visitRunDefers(inst *ssa.RunDefers, fr *frame) {
	for i := len(fr.defers) - 1; i >= 0; i-- {
		fr.callCommon(fr.defers[i].Value(), fr.defers[i].Common())
	}
}

func visitPhi(inst *ssa.Phi, fr *frame) {
	// In the case of channels, find the last defined channel and replace it.
	if _, ok := inst.Type().(*types.Chan); ok {
		//preds := inst.Block().Preds // PredBlocks: order is significant.
		fr.locals[inst], _ = fr.get(inst.Edges[0])
		fr.phi[inst] = inst.Edges
	}
}

func visitTypeAssert(inst *ssa.TypeAssert, fr *frame) {
	if iface, ok := inst.AssertedType.(*types.Interface); ok {
		if meth, _ := types.MissingMethod(inst.X.Type(), iface, true); meth == nil { // No missing methods
			switch vd, kind := fr.get(inst.X); kind {
			case Struct, LocalStruct, Array, LocalArray, Chan:
				fr.tuples[inst] = make(Tuples, 2)
				fr.tuples[inst][0] = vd
				fmt.Fprintf(os.Stderr, "   %s = %s.(type assert %s) iface\n", reg(inst), reg(inst.X), inst.AssertedType.String())
				fmt.Fprintf(os.Stderr, "    ^ defined as %s\n", vd.String())

			default:
				fmt.Fprintf(os.Stderr, "   %s = %s.(type assert %s)\n", red(reg(inst)), reg(inst.X), inst.AssertedType.String())
				fmt.Fprintf(os.Stderr, "    ^ untracked/unknown\n")
			}
			return
		}
	} else { // Concrete type
		if types.Identical(inst.AssertedType.Underlying(), inst.X.Type().Underlying()) {
			switch vd, kind := fr.get(inst.X); kind {
			case Struct, LocalStruct, Array, LocalArray, Chan:
				fr.tuples[inst] = make(Tuples, 2)
				fr.tuples[inst][0] = vd
				fmt.Fprintf(os.Stderr, "   %s = %s.(type assert %s) concrete\n", reg(inst), reg(inst.X), inst.AssertedType.String())
				fmt.Fprintf(os.Stderr, "    ^ defined as %s\n", vd.String())

			default:
				fmt.Fprintf(os.Stderr, "   %s = %s.(type assert %s)\n", red(reg(inst)), reg(inst.X), inst.AssertedType.String())
				fmt.Fprintf(os.Stderr, "    ^ untracked/unknown\n")
			}
			return
		}
	}
	fmt.Fprintf(os.Stderr, "   # %s = %s.(%s) impossible type assertion\n", red(reg(inst)), reg(inst.X), inst.AssertedType.String())
}


================================================
FILE: cmd/buildssa.go
================================================
// Copyright © 2016 Nicholas Ng <nickng@projectfate.org>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
	"log"
	"os"

	"github.com/nickng/dingo-hunter/logwriter"
	"github.com/nickng/dingo-hunter/ssabuilder"
	"github.com/spf13/cobra"
)

// buildssaCmd represents the buildssa command
var buildssaCmd = &cobra.Command{
	Use:   "buildssa",
	Short: "Build SSA IR of the input source files",
	Long:  `Build SSA IR of the input source files`,
	Run: func(cmd *cobra.Command, args []string) {
		Build(args)
	},
}

var (
	dumpSSA bool
	dumpAll bool
)

func init() {
	RootCmd.AddCommand(buildssaCmd)

	buildssaCmd.Flags().BoolVar(&dumpSSA, "dump", false, "dump SSA IR of input files (based on CFG)")
	buildssaCmd.Flags().BoolVar(&dumpAll, "dump-all", false, "dump all SSA IR of input files (including unused)")
	if dumpSSA && dumpAll {
		dumpSSA = false // dumpAll override dumpSSA
	}
}

func Build(files []string) {
	logFile, err := RootCmd.PersistentFlags().GetString("log")
	if err != nil {
		log.Fatal(err)
	}
	noLogging, err := RootCmd.PersistentFlags().GetBool("no-logging")
	if err != nil {
		log.Fatal(err)
	}
	noColour, err := RootCmd.PersistentFlags().GetBool("no-colour")
	if err != nil {
		log.Fatal(err)
	}
	l := logwriter.NewFile(logFile, !noLogging, !noColour)
	if err := l.Create(); err != nil {
		log.Fatal(err)
	}
	defer l.Cleanup()

	conf, err := ssabuilder.NewConfig(files)
	if err != nil {
		log.Fatal(err)
	}
	conf.BuildLog = l.Writer
	ssainfo, err := conf.Build()
	if err != nil {
		log.Fatal(err)
	}
	if dumpSSA {
		if _, err := ssainfo.WriteTo(os.Stdout); err != nil {
			log.Fatal(err)
		}
	}
	if dumpAll {
		if _, err := ssainfo.WriteAll(os.Stdout); err != nil {
			log.Fatal(err)
		}
	}
}


================================================
FILE: cmd/cfsms.go
================================================
// Copyright © 2016 Nicholas Ng <nickng@projectfate.org>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
	"log"

	"github.com/nickng/dingo-hunter/cfsmextract"
	"github.com/nickng/dingo-hunter/logwriter"
	"github.com/nickng/dingo-hunter/ssabuilder"
	"github.com/spf13/cobra"
)

var (
	prefix string // Output files prefix
	outdir string // CFMSs output directory
)

// cfsmsCmd represents the analyse command
var cfsmsCmd = &cobra.Command{
	Use:   "cfsms",
	Short: "Extract CFSMs from source code",
	Long: `Extract CFSMs from source code

The inputs should be a list of .go files in the same directory (of package main)
One of the .go file should contain the main function.`,
	Run: func(cmd *cobra.Command, args []string) {
		extractCFSMs(args)
	},
}

func init() {
	cfsmsCmd.Flags().StringVar(&prefix, "prefix", "output", "Output files prefix")
	cfsmsCmd.Flags().StringVar(&outdir, "outdir", "third_party/gmc-synthesis/inputs", "Output directory for CFSMs")

	RootCmd.AddCommand(cfsmsCmd)
}

func extractCFSMs(files []string) {
	logFile, err := RootCmd.PersistentFlags().GetString("log")
	if err != nil {
		log.Fatal(err)
	}
	noLogging, err := RootCmd.PersistentFlags().GetBool("no-logging")
	if err != nil {
		log.Fatal(err)
	}
	noColour, err := RootCmd.PersistentFlags().GetBool("no-colour")
	if err != nil {
		log.Fatal(err)
	}
	l := logwriter.NewFile(logFile, !noLogging, !noColour)
	if err := l.Create(); err != nil {
		log.Fatal(err)
	}
	defer l.Cleanup()

	conf, err := ssabuilder.NewConfig(files)
	conf.BuildLog = l.Writer
	if err != nil {
		log.Fatal(err)
	}
	ssainfo, err := conf.Build()
	if err != nil {
		log.Fatal(err)
	}
	extract := cfsmextract.New(ssainfo, prefix, outdir)
	go extract.Run()

	select {
	case <-extract.Error:
		log.Fatal(err)
	case <-extract.Done:
		log.Println("Analysis finished in", extract.Time)
		extract.WriteOutput()
	}
}


================================================
FILE: cmd/checkfair.go
================================================
// Copyright © 2016 Nicholas Ng <nickng@projectfate.org>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
	"log"

	"github.com/nickng/dingo-hunter/fairness"
	"github.com/nickng/dingo-hunter/logwriter"
	"github.com/nickng/dingo-hunter/ssabuilder"
	"github.com/spf13/cobra"
)

// checkfairCmd represents the check-fair command
var checkfairCmd = &cobra.Command{
	Use:   "checkfair",
	Short: "Runs loop fairness checks",
	Long: `Runs loop fairness checks

The checks will find potential problematic loops, such as those which are
unbalanced - loop conditions favour infinite iterations deterministically`,
	Run: func(cmd *cobra.Command, args []string) {
		check(args)
	},
}

func init() {
	RootCmd.AddCommand(checkfairCmd)
}

func check(files []string) {
	logFile, err := RootCmd.PersistentFlags().GetString("log")
	if err != nil {
		log.Fatal(err)
	}
	noLogging, err := RootCmd.PersistentFlags().GetBool("no-logging")
	if err != nil {
		log.Fatal(err)
	}
	noColour, err := RootCmd.PersistentFlags().GetBool("no-colour")
	if err != nil {
		log.Fatal(err)
	}
	l := logwriter.NewFile(logFile, !noLogging, !noColour)
	if err := l.Create(); err != nil {
		log.Fatal(err)
	}
	defer l.Cleanup()

	conf, err := ssabuilder.NewConfig(files)
	if err != nil {
		log.Fatal(err)
	}
	conf.BuildLog = l.Writer
	ssainfo, err := conf.Build()
	if err != nil {
		log.Fatal(err)
	}
	fairness.Check(ssainfo)
}


================================================
FILE: cmd/migo.go
================================================
// Copyright © 2016 Nicholas Ng <nickng@projectfate.org>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
	"log"
	"os"

	"github.com/nickng/dingo-hunter/logwriter"
	"github.com/nickng/dingo-hunter/migoextract"
	"github.com/nickng/dingo-hunter/ssabuilder"
	"github.com/nickng/migo/v3/migoutil"
	"github.com/spf13/cobra"
)

var (
	outfile string // Path to output file
)

// migoCmd represents the analyse command
var migoCmd = &cobra.Command{
	Use:   "migo",
	Short: "Extract MiGo types from source code",
	Long: `Extract MiGo types from source code

The inputs should be a list of .go files in the same directory (of package main)
One of the .go file should contain the main function.`,
	Run: func(cmd *cobra.Command, args []string) {
		extractMigo(args)
	},
}

func init() {
	migoCmd.Flags().StringVar(&outfile, "output", "", "output migo file")

	RootCmd.AddCommand(migoCmd)
}

func extractMigo(files []string) {
	logFile, err := RootCmd.PersistentFlags().GetString("log")
	if err != nil {
		log.Fatal(err)
	}
	noLogging, err := RootCmd.PersistentFlags().GetBool("no-logging")
	if err != nil {
		log.Fatal(err)
	}
	noColour, err := RootCmd.PersistentFlags().GetBool("no-colour")
	if err != nil {
		log.Fatal(err)
	}
	l := logwriter.NewFile(logFile, !noLogging, !noColour)
	if err := l.Create(); err != nil {
		log.Fatal(err)
	}
	defer l.Cleanup()

	conf, err := ssabuilder.NewConfig(files)
	if err != nil {
		log.Fatal(err)
	}
	conf.BuildLog = l.Writer
	ssainfo, err := conf.Build()
	if err != nil {
		log.Fatal(err)
	}
	extract, err := migoextract.New(ssainfo, l.Writer)
	if err != nil {
		log.Fatal(err)
	}
	go extract.Run()

	select {
	case <-extract.Error:
		log.Fatal(err)
	case <-extract.Done:
		extract.Logger.Println("Analysis finished in", extract.Time)
	}

	migoutil.SimplifyProgram(extract.Env.MigoProg)
	if outfile != "" {
		f, err := os.Create(outfile)
		if err != nil {
			log.Fatal(err)
		}
		f.WriteString(extract.Env.MigoProg.String())
		defer f.Close()
	} else {
		os.Stdout.WriteString(extract.Env.MigoProg.String())
	}
}


================================================
FILE: cmd/root.go
================================================
// Copyright © 2016 Nicholas Ng <nickng@projectfate.org>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
	"fmt"
	"os"

	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var (
	cfgFile   string // Path to config file
	logFile   string // Path to log file
	noLogging bool   // Turn off logging
	noColour  bool   // Turn of colour output
)

// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
	Use:   "dingo-hunter",
	Short: "Static analyser for deadlock detection",
	Long: `dingo-hunter is a static deadlock detector for Go

This is the toplevel command.
Use "dingo-hunter [command] sources.go..." to analyse source files`,
}

// Execute adds all child commands to the root command sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
	if err := RootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}
}

func init() {
	cobra.OnInitialize(initConfig)

	RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.dingo-hunter.yaml)")
	RootCmd.PersistentFlags().StringVar(&logFile, "log", "", "path to log file (default is stdout)")
	RootCmd.PersistentFlags().BoolVar(&noLogging, "no-logging", false, "disable logging")
	RootCmd.PersistentFlags().BoolVar(&noColour, "no-colour", false, "disable colour output")
}

// initConfig reads in config file and ENV variables if set.
func initConfig() {
	if cfgFile != "" { // enable ability to specify config file via flag
		viper.SetConfigFile(cfgFile)
	}

	viper.SetConfigName(".dingo-hunter") // name of config file (without extension)
	viper.AddConfigPath("$HOME")         // adding home directory as first search path
	viper.AutomaticEnv()                 // read in environment variables that match

	// If a config file is found, read it in.
	if err := viper.ReadInConfig(); err == nil {
		fmt.Println("Using config file:", viper.ConfigFileUsed())
	}
}


================================================
FILE: cmd/serve.go
================================================
// Copyright © 2016 Nicholas Ng <nickng@projectfate.org>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
	"go/build"
	"log"
	"path"

	"github.com/nickng/dingo-hunter/webservice"
	"github.com/spf13/cobra"
)

// serveCmd represents the serve command
var serveCmd = &cobra.Command{
	Use:     "serve",
	Aliases: []string{"server"},
	Short:   "Run an HTTP webservice for demo",
	Long: `Run an HTTP webservice for analysis demo.

The analysis will be presented side-by-side with its source code.
Each example is a Go command (i.e. package main) in a directory, under the examples directory.`,
	Run: func(cmd *cobra.Command, args []string) {
		Serve()
	},
}

const basePkg = "github.com/nickng/dingo-hunter"

var (
	addr string // Listen interface.
	port string // Listen port.
)

func init() {
	RootCmd.AddCommand(serveCmd)

	p, err := build.Default.Import(basePkg, "", build.FindOnly)
	if err != nil {
		log.Fatal("Could not find base path")
	}
	basePath := p.Dir

	serveCmd.Flags().StringVar(&addr, "bind", "127.0.0.1", "Bind address. Defaults to 127.0.0.1.")
	serveCmd.Flags().StringVar(&port, "port", "6060", "Listen port. Defaults to 6060.")
	serveCmd.Flags().StringVar(&webservice.ExamplesDir, "examples", path.Join(basePath, "examples", "popl17"), "Path to examples directory")
	serveCmd.Flags().StringVar(&webservice.TemplateDir, "templates", path.Join(basePath, "templates"), "Path to templates directory")
	serveCmd.Flags().StringVar(&webservice.StaticDir, "static", path.Join(basePath, "static"), "Path to static files directory")
}

// Serve starts the HTTP server.
func Serve() {
	server := webservice.NewServer(addr, port)
	server.Start()
	server.Close()
}


================================================
FILE: doc.go
================================================
// Command dingo-hunter is a tool for analysing Go code to extract the
// communication patterns for deadlock analysis.
//
// The deadlock analysis approach is based on multiparty session types and
// global graph synthesis POPL'15 (Lange, Tuosto, Yoshida) and CC'16 (Ng,
// Yoshida).
package main


================================================
FILE: examples/altbit/main.go
================================================
package main

// Alternating bit - from Milner's Communication and Concurrency
// with (meaningless) timing

import (
	"fmt"
	"time"
)

func main() {
	trans := make(chan int, 1)
	ack := make(chan int, 1)
	go tx(trans, ack)
	rx(ack, trans)
}

func flip(b int) int {
	return (b + 1) % 2
}

func tx(snd chan<- int, ack <-chan int) {
	b := 0
	for {
		fmt.Printf("tx[%d]: accept\n", b)
		fmt.Printf("tx[%d]: send[%d]\n", b, b)
		snd <- b
	SENDING:
		for { // SENDING[b]
			select {
			case x := <-ack:
				if x == b {
					fmt.Printf("tx[%d]: ack[b]\n", b)
					b = flip(b)
					break SENDING // ACCEPT !b
				} else {
					fmt.Printf("tx[%d]: ack[!b]\n", b)
					b = flip(b)
					// SENDING b
				}
			case <-time.After(1 * time.Second):
				fmt.Printf("tx[%d]: timeout\n", b)
				fmt.Printf("tx[%d]: send[%d]\n", b, b)
				snd <- b
				// SENDING b
			}
		}
	}
}

func rx(reply chan<- int, trans <-chan int) {
	b := 1
	for {
		fmt.Printf("rx[%d]: deliver\n", b)
		fmt.Printf("rx[%d]: reply[%d]\n", b, b)
		reply <- b
	REPLYING:
		for {
			select { // REPLYING[b]
			case x := <-trans:
				if x != b {
					fmt.Printf("rx[%d]: trans[!b]\n", b)
					break REPLYING // DELIVER !b
				} else {
					fmt.Printf("rx[%d]: trans[b]\n", b)
					// REPLYING b
				}
			case <-time.After(1 * time.Second):
				fmt.Printf("rx[%d]: timeout\n", b)
				fmt.Printf("rx[%d]: reply[%d]\n", b, b)
				reply <- b
				// REPLYING b
			}
		}
	}
}


================================================
FILE: examples/branch-dependent-deadlock/main.go
================================================
package main

func S(ch chan int, done chan struct{}) {
	ch <- 1
	done <- struct{}{}
}

func R(ch chan int, done chan struct{}) {
	<-ch
	done <- struct{}{}
}

func main() {
	done := make(chan struct{})
	for i := 0; i < 10; i++ {
		ch := make(chan int)
		if i%2 == 0 {
			go S(ch, done)
		} else {
			go R(ch, done)
		}
	}
	<-done
}


================================================
FILE: examples/channel-scoping-test/main.go
================================================
// The reentry example targets multiple use of a function with channel created
// inside. The channel is anchored in the function, so multiple calls of the
// function will use different version of the channel(s). Combined with loop
// indices assumptions this will be inaccurate.

package main

func main() {
	ch := makenew()
	for i := 0; i < 2; i++ {
		ch2 := makenew()
		ch2 <- 22
	}
	ch <- 42
}

func makenew() chan int {
	return make(chan int, 1)
}


================================================
FILE: examples/commaok/main.go
================================================
package main

import (
	"fmt"
)

func main() {
	ch := make(chan int, 1)
	ch <- 1
	y := make(map[int]int)
	if x, ok := <-ch; ok {
		y[x] = 1
		if z, ok := y[1]; ok {
			fmt.Println(z)
		}
	}
}


================================================
FILE: examples/cond-recur/main.go
================================================
// Command conditional-recur has a recursion with conditional on one goroutine
// and another receiving until a done message is received.
package main

import "fmt"

func x(ch chan int, done chan struct{}) {
	i := 0
	for {
		if i < 3 {
			ch <- i
			fmt.Println("Sent", i)
			i++
		} else {
			done <- struct{}{}
			return
		}
	}
}

func main() {
	done := make(chan struct{})
	ch := make(chan int)
	go x(ch, done)
FINISH:
	for {
		select {
		case x := <-ch:
			fmt.Println(x)
		case <-done:
			break FINISH
		}
	}
}


================================================
FILE: examples/deadlocking-philosophers/main.go
================================================
package main

// Dining Philosopher modified to include a deadlock.
// https://github.com/doug/go-dining-philosophers

import (
	"fmt"
	"math/rand"
	"time"
)

type Philosopher struct {
	name      string
	chopstick chan bool
	neighbor  *Philosopher
}

func makePhilosopher(name string, neighbor *Philosopher) *Philosopher {
	phil := &Philosopher{name, make(chan bool, 1), neighbor}
	phil.chopstick <- true
	return phil
}

func (phil *Philosopher) think() {
	fmt.Printf("%v is thinking.\n", phil.name)
	time.Sleep(time.Duration(rand.Int63n(1e9)))
}

func (phil *Philosopher) eat() {
	fmt.Printf("%v is eating.\n", phil.name)
	time.Sleep(time.Duration(rand.Int63n(1e9)))
}

func (phil *Philosopher) getChopsticks() {
	timeout := make(chan bool, 1)
	go func() { time.Sleep(1e9); timeout <- true }()
	<-phil.chopstick
	fmt.Printf("%v got his chopstick.\n", phil.name)
	// XXX Deadlock may happen here.
	fmt.Printf("%v got %v's chopstick.\n", phil.name, phil.neighbor.name)
	fmt.Printf("%v has two chopsticks.\n", phil.name)
	return
}

func (phil *Philosopher) returnChopsticks() {
	phil.chopstick <- true
	phil.neighbor.chopstick <- true
}

func (phil *Philosopher) dine(announce chan *Philosopher) {
	phil.think()
	phil.getChopsticks()
	phil.eat()
	phil.returnChopsticks()
	announce <- phil
}

func main() {
	//names := []string{"Kant", "Heidegger", "Wittgenstein",
	//	"Locke", "Descartes", "Newton", "Hume", "Leibniz"}
	names := []string{"Phil"}
	philosophers := make([]*Philosopher, len(names))
	var phil *Philosopher
	for i, name := range names {
		phil = makePhilosopher(name, phil)
		philosophers[i] = phil
	}
	philosophers[0].neighbor = phil
	fmt.Printf("There are %v philosophers sitting at a table.\n", len(philosophers))
	fmt.Println("They each have one chopstick, and must borrow from their neighbor to eat.")
	announce := make(chan *Philosopher)
	for _, phil := range philosophers {
		go phil.dine(announce)
	}
	for i := 0; i < len(names); i++ {
		phil := <-announce
		fmt.Printf("%v is done dining.\n", phil.name)
	}
}


================================================
FILE: examples/dining-philosophers/main.go
================================================
package main

// Dining Philosopher.
// https://github.com/doug/go-dining-philosophers

import (
	"fmt"
	"math/rand"
	"time"
)

type Philosopher struct {
	name      string
	chopstick chan bool
	neighbor  *Philosopher
}

func makePhilosopher(name string, neighbor *Philosopher) *Philosopher {
	phil := &Philosopher{name, make(chan bool, 1), neighbor}
	phil.chopstick <- true
	return phil
}

func (phil *Philosopher) think() {
	fmt.Printf("%v is thinking.\n", phil.name)
	time.Sleep(time.Duration(rand.Int63n(1e9)))
}

func (phil *Philosopher) eat() {
	fmt.Printf("%v is eating.\n", phil.name)
	time.Sleep(time.Duration(rand.Int63n(1e9)))
}

func (phil *Philosopher) getChopsticks() {
	timeout := make(chan bool, 1)
	go func() { time.Sleep(1e9); timeout <- true }()
	<-phil.chopstick
	fmt.Printf("%v got his chopstick.\n", phil.name)
	select {
	case <-phil.neighbor.chopstick:
		fmt.Printf("%v got %v's chopstick.\n", phil.name, phil.neighbor.name)
		fmt.Printf("%v has two chopsticks.\n", phil.name)
		return
	case <-timeout:
		phil.chopstick <- true
		phil.think()
		phil.getChopsticks()
	}
}

func (phil *Philosopher) returnChopsticks() {
	phil.chopstick <- true
	phil.neighbor.chopstick <- true
}

func (phil *Philosopher) dine(announce chan *Philosopher) {
	phil.think()
	phil.getChopsticks()
	phil.eat()
	phil.returnChopsticks()
	announce <- phil
}

func main() {
	names := []string{"Kant", "Heidegger", "Wittgenstein",
		"Locke", "Descartes", "Newton", "Hume", "Leibniz"}
	philosophers := make([]*Philosopher, len(names))
	var phil *Philosopher
	for i, name := range names {
		phil = makePhilosopher(name, phil)
		philosophers[i] = phil
	}
	philosophers[0].neighbor = phil
	fmt.Printf("There are %v philosophers sitting at a table.\n", len(philosophers))
	fmt.Println("They each have one chopstick, and must borrow from their neighbor to eat.")
	announce := make(chan *Philosopher)
	for _, phil := range philosophers {
		go phil.dine(announce)
	}
	for i := 0; i < len(names); i++ {
		phil := <-announce
		fmt.Printf("%v is done dining.\n", phil.name)
	}
}


================================================
FILE: examples/factorial/main.go
================================================
package main

import "fmt"

func main() {
	ch := make(chan int)
	go fact(5, ch)
	fmt.Println(<-ch)
}

func fact(n int, results chan<- int) {
	if n <= 1 {
		results <- n
		return
	}
	ch := make(chan int)
	go fact(n-1, ch)
	results <- n * <-ch
}


================================================
FILE: examples/fanin-pattern/main.go
================================================
package main

import (
	"fmt"
)

func work(out chan<- int) {
	for {
		out <- 42
	}
}

func fanin(ch1, ch2 <-chan int) <-chan int {
	c := make(chan int)
	go func() {
		for {
			select {
			case s := <-ch1:
				c <- s
			case s := <-ch2:
				c <- s
			}
		}
	}()
	return c
}

func main() {
	input1 := make(chan int)
	input2 := make(chan int)
	go work(input1)
	go work(input2)
	c := fanin(input1, input2)
	for {
		fmt.Println(<-c)
	}
}


================================================
FILE: examples/fanin-pattern-commaok/main.go
================================================
package main

// fanin pattern, using for-range loop to consume values (syntactic sugar of
// loop over r, ok := <-ch)

import (
	"fmt"
	"time"
)

func work(out chan<- int) {
	for {
		out <- 42
	}
}

func fanin(ch1, ch2 <-chan int) <-chan int {
	c := make(chan int)
	go func(ch1, ch2 <-chan int, c chan<- int) {
		for {
			select {
			case s := <-ch1:
				c <- s
			case s := <-ch2:
				c <- s
			default:
				close(c)
				return
			}
		}
	}(ch1, ch2, c)
	return c
}

func main() {
	input1 := make(chan int)
	input2 := make(chan int)
	go work(input1)
	go work(input2)
	time.Sleep(1 * time.Second)
	for v := range fanin(input1, input2) {
		fmt.Println(v)
	}
}


================================================
FILE: examples/fcall/main.go
================================================
package main

import "fmt"

var ch chan int

func f() {
	fmt.Println("blah")
	g()
}

func g() {
	<-ch
	fmt.Println("blah-g")
}

func main() {
	fmt.Println("before")
	ch = make(chan int)
	f()
	g()
	x := func() <-chan int { return make(chan int) }
	select {
	case <-x():
	}
	fmt.Println("after")
}


================================================
FILE: examples/forselect/main.go
================================================
// Command nodet-for-select is a for-select pattern between two compatible
// recursive select.
package main

import "fmt"

func sel1(ch1, ch2 chan int, done chan struct{}) {
	for {
		select {
		case <-ch1:
			fmt.Println("sel1: recv")
			done <- struct{}{}
			return
		case ch2 <- 1:
			fmt.Println("sel1: send")
		}
	}
}

func sel2(ch1, ch2 chan int, done chan struct{}) {
	for {
		select {
		case <-ch2:
			fmt.Println("sel2: recv")
		case ch1 <- 2:
			fmt.Println("sel2: send")
			done <- struct{}{}
			return
		}
	}
}

func main() {
	done := make(chan struct{})
	a := make(chan int)
	b := make(chan int)
	go sel1(a, b, done)
	go sel2(a, b, done)

	<-done
	<-done
}


================================================
FILE: examples/giachino-concur14-dining-philosopher/main.go
================================================
package main

// Example from CONCUR 14 paper by Giachino et al.
// doi: 10.1007/978-3-662-44584-6_6

import (
	"fmt"
	"time"
)

func Fork(fork chan int) {
	for {
		fork <- 1
		<-fork
	}
}

// philosophers (infinite recursive).
func phil(fork1, fork2 chan int, id int) {
	var x1, x2 int
	for {
		select {
		case x1 = <-fork1:
			select {
			case x2 = <-fork2:
				fmt.Printf("phil %d got both fork\n", id)
				fork1 <- x1
				fork2 <- x2
			default:
				fork1 <- x1
			}
		case x1 = <-fork2:
			select {
			case x2 = <-fork1:
				fmt.Printf("phil %d got both fork\n", id)
				fork2 <- x1
				fork1 <- x2
			default:
				fork2 <- x1
			}
		}
	}
}

func main() {
	fork1 := make(chan int)
	fork2 := make(chan int)
	fork3 := make(chan int)
	go Fork(fork1)
	go Fork(fork2)
	go Fork(fork3)
	go phil(fork1, fork2, 0) // deadlock if phil(fork2, fork1, 0)
	go phil(fork2, fork3, 1)
	go phil(fork3, fork1, 2)
	time.Sleep(10 * time.Second)
}


================================================
FILE: examples/giachino-concur14-factorial/main.go
================================================
package main

import "fmt"

// Example from CONCUR 14 paper by Giachino et al.
// doi: 10.1007/978-3-662-44584-6_6

func fact(n int, r, s chan int) {
	if n == 0 {
		m := <-r
		s <- m
		return
	}
	t := make(chan int, 1)
	fact(n-1, t, s)
	m := <-r
	t <- m * n
}

func main() {
	accumulated, result := make(chan int, 1), make(chan int)
	go fact(3, accumulated, result)
	accumulated <- 1
	fmt.Println(<-result)
}


================================================
FILE: examples/github-golang-go-issue-12734/main.go
================================================
package main

// This is a bug report from golang/go which the deadlock detector does not
// detect a deadlock because the net pacakge is loaded (disables detector).

import (
	"fmt"
	"net/http"
)

func useless(address string) []byte {
	http.Get("https://www.google.com/")
	return nil
}

func test_a(test_channel chan int) {
	test_channel <- 1
	return
}

func test() {
	test_channel := make(chan int)
	for i := 0; i < 10; i++ {
		go test_a(test_channel)
	}
	for {
		fmt.Println(<-test_channel)
	}
}
func main() {
	test()
}


================================================
FILE: examples/golang-blog-prime-sieve/main.go
================================================
// Command golang-blog-prime-sieve is an example from Golang blog.
// https://golang.org/doc/play/sieve.go
//
package main

import (
	"fmt"
)

// Send the sequence 2, 3, 4, ... to channel 'ch'.
func Generate(ch chan<- int) {
	for i := 2; ; i++ {
		ch <- i // Send 'i' to channel 'ch'.
	}
}

// Copy the values from channel 'in' to channel 'out',
// removing those divisible by 'prime'.
func Filter(in <-chan int, out chan<- int, prime int) {
	for {
		i := <-in // Receive value from 'in'.
		if i%prime != 0 {
			out <- i // Send 'i' to 'out'.
		}
	}
}

// The prime sieve: Daisy-chain Filter processes.
func main() {
	ch := make(chan int) // Create a new channel.
	go Generate(ch)      // Launch Generate goroutine.
	for i := 0; i < 10; i++ {
		prime := <-ch
		fmt.Println(prime)
		ch1 := make(chan int)
		go Filter(ch, ch1, prime)
		ch = ch1
	}
}


================================================
FILE: examples/infinite-prime-sieve/main.go
================================================
// Command infinite-primesieve is a modified primesieve example from Golang
// blog. The program generates an infinite list  (instead of a fixed number) of
// prime numbers.
//
// Original: https://golang.org/doc/play/sieve.go
//
package main

import (
	"fmt"
)

// Send the sequence 2, 3, 4, ... to channel 'ch'.
func Generate(ch chan<- int) {
	for i := 2; ; i++ {
		ch <- i // Send 'i' to channel 'ch'.
	}
}

// Copy the values from channel 'in' to channel 'out',
// removing those divisible by 'prime'.
func Filter(in <-chan int, out chan<- int, prime int) {
	for {
		i := <-in // Receive value from 'in'.
		if i%prime != 0 {
			out <- i // Send 'i' to 'out'.
		}
	}
}

// The prime sieve: Daisy-chain Filter processes.
func main() {
	ch := make(chan int) // Create a new channel.
	go Generate(ch)      // Launch Generate goroutine.
	for i := 0; ; i++ {
		prime := <-ch
		fmt.Println(prime)
		ch1 := make(chan int)
		go Filter(ch, ch1, prime)
		ch = ch1
	}
}


================================================
FILE: examples/issue-10-close-wrong-migo-chan-name/main.go
================================================
package main

// Issue #10, when generating migo from programs that uses close, the channel
// name used is incorrect.

func main() {
	x := make(chan bool)
	close(x)
}


================================================
FILE: examples/issue-11-non-communicating-fn-call/main.go
================================================
package main

// Issue #11, when a function is empty, all 'call' on that def in a migo file
// should be scrubbed.

func main() {
	x := make(chan bool)
	go func() { x <- true }()
	if true {
		<-x
	}
}


================================================
FILE: examples/jobsched/main.go
================================================
package main

import (
	"fmt"
	"time"
)

var i int

func worker(id int, jobQueue <-chan int, done <-chan struct{}) {
	for {
		select {
		case jobID := <-jobQueue:
			fmt.Println(id, "Executing job", jobID)
		case <-done:
			fmt.Println(id, "Quits")
			return
		}
	}
}

func morejob() bool {
	i++
	return i < 20
}

func producer(q chan int, done chan struct{}) {
	for morejob() {
		q <- i
	}
	close(done)
}

func main() {
	jobQueue := make(chan int)
	done := make(chan struct{})
	go worker(1, jobQueue, done)
	go worker(2, jobQueue, done)
	producer(jobQueue, done)
	time.Sleep(1 * time.Second)
}


================================================
FILE: examples/local-deadlock/main.go
================================================
package main

import (
	"fmt"
	"time"
)

func Work() {
	for {
		fmt.Println("Working")
		time.Sleep(1 * time.Second)
	}
}

func Send(ch chan<- int)                  { ch <- 42 }
func Recv(ch <-chan int, done chan<- int) { done <- <-ch }

func main() {
	ch, done := make(chan int), make(chan int)
	go Send(ch)
	go Recv(ch, done)
	go Recv(ch, done)
	go Work()

	<-done
	<-done
}


================================================
FILE: examples/local-deadlock-fixed/main.go
================================================
package main

import (
	"fmt"
	"time"
)

func Work() {
	for {
		fmt.Println("Working")
		time.Sleep(1 * time.Second)
	}
}

func Send(ch chan<- int)                  { ch <- 42 }
func Recv(ch <-chan int, done chan<- int) { done <- <-ch }

func main() {
	ch, done := make(chan int), make(chan int)
	go Send(ch)
	go Recv(ch, done)
	go Work()

	<-done
}


================================================
FILE: examples/loop-variations/main.go
================================================
package main

// This example test different loop and is used for checking loop SSA
// generation.

import "fmt"

func main() {
	/*
			xs := []int{1, 2, 3}
			for _, s := range xs {
				fmt.Println(s)
			}

			xs2 := [3]int{1, 2, 3}
			for _, s := range xs2 {
				fmt.Println(s)
			}

			for i := 0; i < 3; i++ {
				fmt.Println("xs[i]", xs[i])
			}

			for {
				fmt.Println("looooopppp one") // This executes once
				break
			}

		loopcond := func(i int) bool { return false }

		for k := 0; loopcond(k); k++ {
			fmt.Println("Loop k: ", k)
		}
	*/
	for i := 0; i < 3; i++ {
		for j := 0; j < 2; j++ {
			fmt.Printf("Index (%d, %d) ", i, j)
			x := make(chan int)
			<-x
		}
		fmt.Printf("ASBCD")
	}
	/*
		x := []int{1, 2, 3, 4}
		for i := range x { // Range loop (safe)
			fmt.Println(i)
		}

		ch := make(chan int)
		go func(ch chan int) { ch <- 42; close(ch) }(ch)
		for v := range ch {
			fmt.Println(v)
		}

		for {
			fmt.Println("Infinite looooopppp")
		}
	*/
}


================================================
FILE: examples/makechan-in-loop/main.go
================================================
// The loop-chan example shows what will happen when a channel is created in a
// loop (and stored in slices). Pointer analysis identifies the channel
// operations with channels created inside the loop, but the loop indices are
// ignored and the analysis must make an assumption that the channels inside the
// slices are accessed in order.

package main

func main() {
	chans := make([]chan int, 5)
	for i := range chans {
		chans[i] = make(chan int, 1)
	}

	for _, ch := range chans {
		ch <- 42
	}

	for _, ch := range chans {
		<-ch
	}
}


================================================
FILE: examples/md5/main.go
================================================
// +build OMIT

package main

import (
	"crypto/md5"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"sync"
)

// A result is the product of reading and summing a file using MD5.
type result struct {
	path string
	sum  [md5.Size]byte
	err  error
}

// sumFiles starts goroutines to walk the directory tree at root and digest each
// regular file.  These goroutines send the results of the digests on the result
// channel and send the result of the walk on the error channel.  If done is
// closed, sumFiles abandons its work.
func sumFiles(done <-chan struct{}, root string) (<-chan result, <-chan error) {
	// For each regular file, start a goroutine that sums the file and sends
	// the result on c.  Send the result of the walk on errc.
	c := make(chan result)
	errc := make(chan error, 1)
	go func() { // HL
		var wg sync.WaitGroup
		err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}
			if !info.Mode().IsRegular() {
				return nil
			}
			wg.Add(1)
			go func() { // HL
				data, err := ioutil.ReadFile(path)
				select {
				case c <- result{path, md5.Sum(data), err}: // HL
				case <-done: // HL
				}
				wg.Done()
			}()
			// Abort the walk if done is closed.
			select {
			case <-done: // HL
				return errors.New("walk canceled")
			default:
				return nil
			}
		})
		// Walk has returned, so all calls to wg.Add are done.  Start a
		// goroutine to close c once all the sends are done.
		go func() { // HL
			wg.Wait()
			close(c) // HL
		}()
		// No select needed here, since errc is buffered.
		errc <- err // HL
	}()
	return c, errc
}

// MD5All reads all the files in the file tree rooted at root and returns a map
// from file path to the MD5 sum of the file's contents.  If the directory walk
// fails or any read operation fails, MD5All returns an error.  In that case,
// MD5All does not wait for inflight read operations to complete.
func MD5All(root string) (map[string][md5.Size]byte, error) {
	// MD5All closes the done channel when it returns; it may do so before
	// receiving all the values from c and errc.
	done := make(chan struct{}) // HLdone
	defer close(done)           // HLdone

	c, errc := sumFiles(done, root) // HLdone

	m := make(map[string][md5.Size]byte)
	for r := range c { // HLrange
		if r.err != nil {
			return nil, r.err
		}
		m[r.path] = r.sum
	}
	if err := <-errc; err != nil {
		return nil, err
	}
	return m, nil
}

func main() {
	// Calculate the MD5 sum of all files under the specified directory,
	// then print the results sorted by path name.
	m, err := MD5All(os.Args[1])
	if err != nil {
		fmt.Println(err)
		return
	}
	var paths []string
	for path := range m {
		paths = append(paths, path)
	}
	sort.Strings(paths)
	for _, path := range paths {
		fmt.Printf("%x  %s\n", m[path], path)
	}
}


================================================
FILE: examples/multi-makechan-same-var/main.go
================================================
// createchan is an example which reuses a channel for different operations in
// an expanded form.

package main

func createChan() chan int {
	return make(chan int, 1)
}

func main() {
	ch := createChan()
	ch <- 42

	ch = createChan()
	ch <- 3
}


================================================
FILE: examples/multiple-files/main.go
================================================
package main

import (
	"fmt"
)

func main() {
	fmt.Println("Do stuff")
	x()
	fmt.Println("Do more stuff")
}


================================================
FILE: examples/multiple-files/x.go
================================================
package main

import "fmt"

func x() {
	fmt.Println("Hello X")
}


================================================
FILE: examples/multiple-timeout/main.go
================================================
// Command multiple-timeout is an example which uses multiple branches of
// time.After.
//
// The main purpose is to test if the extracted local type can distinguish
// between two "externally created" channels (in time.After), and are
// initialised separately in the local graph.
package main

import "time"

func main() {
	ch := make(chan int, 1)
	go func(ch chan int) { time.Sleep(10 * time.Second); ch <- 42 }(ch)

	select {
	case <-ch:
	case <-time.After(2 * time.Second):
	case <-time.After(4 * time.Second):
	}
}


================================================
FILE: examples/parallel-buffered-recursive-fibonacci/main.go
================================================
// Command parallel-recursive-fibonacci is a recursive fibonacci which spawns a
// new goroutine per fib call.
package main

import "fmt"

func main() {
	ch := make(chan int)
	go fib(10, ch)
	fmt.Println(<-ch)
}

func fib(n int, ch chan<- int) {
	if n <= 1 {
		ch <- n
		return
	}
	ch1 := make(chan int, 2)
	go fib(n-1, ch1)
	go fib(n-2, ch1)
	ch <- <-ch1 + <-ch1
}


================================================
FILE: examples/parallel-recursive-fibonacci/main.go
================================================
// Command parallel-recursive-fibonacci is a recursive fibonacci which spawns a
// new goroutine per fib call.
package main

import "fmt"

func main() {
	ch := make(chan int)
	go fib(10, ch)
	fmt.Println(<-ch)
}

func fib(n int, ch chan<- int) {
	if n <= 1 {
		ch <- n
		return
	}
	ch1 := make(chan int)
	ch2 := make(chan int)
	go fib(n-1, ch1)
	go fib(n-2, ch2)
	ch <- <-ch1 + <-ch2
}


================================================
FILE: examples/parallel-twoprocess-fibonacci/main.go
================================================
// Command parallel-twoprocess-fibonacci is an improved version of parallel
// fibonacci which limits to only spawning 2 goroutines.
package main

import "fmt"

func fib(n int) int {
	if n <= 1 {
		return n
	}
	return fib(n-1) + fib(n-2)
}

func fibParallel(n int, ch chan<- int) {
	ch <- fib(n)
}

func main() {
	ch1 := make(chan int)
	ch2 := make(chan int)
	n := 10
	go fibParallel(n-1, ch1)
	go fibParallel(n-2, ch2)

	fmt.Println(<-ch1 + <-ch2)
}


================================================
FILE: examples/philo/main.go
================================================
package main

// philio test case from Stadtmuller, Thieman

import (
	"fmt"
)

func philo(id int, forks chan int) {
	for {
		<-forks
		<-forks
		fmt.Printf("%d eats\n", id)
		forks <- 1
		forks <- 1
	}
}

func main() {
	forks := make(chan int)
	go func() { forks <- 1 }()
	go func() { forks <- 1 }()
	go func() { forks <- 1 }()
	go philo(1, forks)
	go philo(2, forks)
	philo(3, forks)
}


================================================
FILE: examples/popl17/alt-bit/main.go
================================================
package main

// Alternating bit - from Milner's Communication and Concurrency

import (
	"fmt"
)

func main() {
	trans := make(chan int, 1)
	ack := make(chan int, 1)
	go tx(trans, ack)
	rx(ack, trans)
}

func tx(snd chan<- int, ack <-chan int) {
	b := 0
	for {
		fmt.Printf("tx[%d]: accept\n", b)
		fmt.Printf("tx[%d]: send[%d]\n", b, b)
		snd <- b
	SENDING:
		for { // SENDING[b]
			select {
			case x := <-ack:
				if x == b {
					fmt.Printf("tx[%d]: ack[b]\n", b)
					b = (b + 1) % 2
					break SENDING // ACCEPT !b
				} else {
					fmt.Printf("tx[%d]: ack[!b]\n", b)
					b = (b + 1) % 2
					// SENDING b
				}
			case snd <- b:
				fmt.Printf("tx[%d]: timeout\n", b)
				fmt.Printf("tx[%d]: send[%d]\n", b, b)
				// SENDING b
			}
		}
	}
}

func rx(reply chan<- int, trans <-chan int) {
	b := 1
	for {
		fmt.Printf("rx[%d]: deliver\n", b)
		fmt.Printf("rx[%d]: reply[%d]\n", b, b)
		reply <- b
	REPLYING:
		for {
			select { // REPLYING[b]
			case x := <-trans:
				if x != b {
					fmt.Printf("rx[%d]: trans[!b]\n", b)
					break REPLYING // DELIVER !b
				} else {
					fmt.Printf("rx[%d]: trans[b]\n", b)
					// REPLYING b
				}
			case reply <- b:
				fmt.Printf("rx[%d]: timeout\n", b)
				fmt.Printf("rx[%d]: reply[%d]\n", b, b)
				// REPLYING b
			}
		}
	}
}


================================================
FILE: examples/popl17/concsys/main.go
================================================
package main

import (
	"fmt"
	"math/rand"
	"time"
)

type Result string
type Search func(query string) Result

func fakeSearch(kind string) Search {
	return func(query string) Result {
		time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
		return Result(fmt.Sprintf("%s result for %q\n", kind, query))
	}
}

var (
	Web    = fakeSearch("web")
	Web1   = fakeSearch("web-1")
	Web2   = fakeSearch("web-2")
	Image  = fakeSearch("image")
	Image1 = fakeSearch("image-1")
	Image2 = fakeSearch("image-2")
	Image3 = fakeSearch("image-3")
	Video  = fakeSearch("video")
	Video1 = fakeSearch("video-1")
	Video2 = fakeSearch("video-2")
	Video3 = fakeSearch("video-3")
)

func SequentialSearch(query string) (results []Result) {
	results = append(results, Web(query))
	results = append(results, Image(query))
	results = append(results, Video(query))
	return
}

func ConcurrentSearch(query string) (results []Result) {
	c := make(chan Result)
	go func(c chan Result) { c <- Web(query) }(c)
	go func(c chan Result) { c <- Image(query) }(c)
	go func(c chan Result) { c <- Video(query) }(c)

	for i := 0; i < 3; i++ {
		result := <-c
		results = append(results, result)
	}
	return
}

func ConcurrentSearchWithCutOff(query string) (results []Result) {
	c := make(chan Result)
	go func() { c <- Web(query) }()
	go func() { c <- Image(query) }()
	go func() { c <- Video(query) }()

	timeout := time.After(80 * time.Millisecond)
	for i := 0; i < 3; i++ { // for each goroutine that is ready pick up results
		select {
		case result := <-c:
			results = append(results, result)
		case <-timeout:
			fmt.Println("timed out")
			return
		}
	}
	return
}

func First(query string, replicas ...Search) Result {
	c := make(chan Result, 1)
	searchReplica := func(i int) { c <- replicas[i](query) }
	for i := range replicas {
		searchReplica(i)
	}
	return <-c
}

func ReplicaSearch(query string) (results []Result) {
	c := make(chan Result)
	go func(c chan Result) { c <- Web1(query) }(c)
	go func(c chan Result) { c <- Image1(query) }(c)
	go func(c chan Result) { c <- Video1(query) }(c)

	timeout := time.After(80 * time.Millisecond)
	//for i := 0; i < 3; i++ {
	select {
	case result := <-c:
		results = append(results, result)
	case <-timeout:
		fmt.Println("seach timed out")
		return
	}
	//}
	return
}

func main() {
	rand.Seed(time.Now().UnixNano())
	start := time.Now()

	results := SequentialSearch("golang")
	//results := ConcurrentSearch("golang")
	//	results := ConcurrentSearchWithCutOff("golang")
	//	results := First("golang", fakeSearch("replica-1"), fakeSearch("replica-2"))
	results = ReplicaSearch("golang")

	elapsed := time.Since(start)
	fmt.Println(results)
	fmt.Println(elapsed)
}


================================================
FILE: examples/popl17/cond-recur/main.go
================================================
// Command conditional-recur has a recursion with conditional on one goroutine
// and another receiving until a done message is received.
package main

import "fmt"

func x(ch chan int, done chan struct{}) {
	i := 0
	for {
		if i < 3 {
			ch <- i
			fmt.Println("Sent", i)
			i++
		} else {
			done <- struct{}{}
			return
		}
	}
}

func main() {
	done := make(chan struct{})
	ch := make(chan int)
	go x(ch, done)
FINISH:
	for {
		select {
		case x := <-ch:
			fmt.Println(x)
		case <-done:
			break FINISH
		}
	}
}


================================================
FILE: examples/popl17/dinephil/main.go
================================================
package main

// Example from CONCUR 14 paper by Giachino et al.
// doi: 10.1007/978-3-662-44584-6_6

import (
	"fmt"
	"time"
)

func Fork(fork chan int) {
	for {
		fork <- 1
		<-fork
	}
}

// philosophers (infinite recursive).
func phil(fork1, fork2 chan int, id int) {
	var x1, x2 int
	for {
		select {
		case x1 = <-fork1:
			select {
			case x2 = <-fork2:
				fmt.Printf("phil %d got both fork\n", id)
				fork1 <- x1
				fork2 <- x2
			default:
				fork1 <- x1
			}
		case x1 = <-fork2:
			select {
			case x2 = <-fork1:
				fmt.Printf("phil %d got both fork\n", id)
				fork2 <- x1
				fork1 <- x2
			default:
				fork2 <- x1
			}
		}
	}
}

func main() {
	fork1 := make(chan int)
	fork2 := make(chan int)
	fork3 := make(chan int)
	go phil(fork1, fork2, 0) // deadlock if phil(fork2, fork1, 0)
	go phil(fork2, fork3, 1)
	go phil(fork3, fork1, 2)
	go Fork(fork1)
	go Fork(fork2)
	go Fork(fork3)
	time.Sleep(10 * time.Second)
}


================================================
FILE: examples/popl17/fact/main.go
================================================
package main

import "fmt"

func main() {
	ch := make(chan int)
	go fact(5, ch)
	fmt.Println(<-ch)
}

func fact(n int, results chan<- int) {
	if n <= 1 {
		results <- n
		return
	}
	ch := make(chan int)
	go fact(n-1, ch)
	results <- n * <-ch
}


================================================
FILE: examples/popl17/fanin/main.go
================================================
package main

import (
	"fmt"
)

func work1(out chan<- int) {
	for {
		out <- 42
	}
}
func work2(out chan<- int) {
	for {
		out <- 42
	}
}

func fanin(ch1, ch2, c chan int) {
	go func(ch1, ch2, c chan int) {
		for {
			select {
			case s := <-ch1:
				c <- s
			case s := <-ch2:
				c <- s
			}
		}
	}(ch1, ch2, c)
	for {
		fmt.Println(<-c)
	}
}

func main() {
	input1 := make(chan int)
	input2 := make(chan int)
	go work1(input1)
	go work2(input2)
	c := make(chan int)
	fanin(input1, input2, c)
}


================================================
FILE: examples/popl17/fanin-alt/main.go
================================================
package main

// fanin pattern, using for-range loop to consume values (syntactic sugar of
// loop over r, ok := <-ch)

import (
	"fmt"
	"time"
)

func work(out chan<- int) {
	for {
		out <- 42
	}
}

func fanin(ch1, ch2, c chan int) {
	go func(ch1, ch2, c chan int) {
		for {
			select {
			case s := <-ch1:
				c <- s
			case s := <-ch2:
				c <- s
			default:
				close(c)
				return
			}
		}
	}(ch1, ch2, c)
	for v := range c {
		fmt.Println(v)
	}
}

func main() {
	input1 := make(chan int)
	input2 := make(chan int)
	go work(input1)
	go work(input2)
	c := make(chan int)
	fanin(input1, input2, c)
	time.Sleep(1 * time.Second)
}


================================================
FILE: examples/popl17/fib/main.go
================================================
// Command parallel-recursive-fibonacci is a recursive fibonacci which spawns a
// new goroutine per fib call.
package main

import "fmt"

func main() {
	ch := make(chan int)
	go fib(10, ch)
	fmt.Println(<-ch)
}

func fib(n int, ch chan<- int) {
	if n <= 1 {
		ch <- n
		return
	}
	ch1 := make(chan int)
	ch2 := make(chan int)
	go fib(n-1, ch1)
	go fib(n-2, ch2)
	ch <- <-ch1 + <-ch2
}


================================================
FILE: examples/popl17/fib-async/main.go
================================================
// Command parallel-recursive-fibonacci is a recursive fibonacci which spawns a
// new goroutine per fib call.
package main

import "fmt"

func main() {
	ch := make(chan int)
	go fib(10, ch)
	fmt.Println(<-ch)
}

func fib(n int, ch chan<- int) {
	if n <= 1 {
		ch <- n
		return
	}
	ch1 := make(chan int, 2)
	go fib(n-1, ch1)
	go fib(n-2, ch1)
	ch <- <-ch1 + <-ch1
}


================================================
FILE: examples/popl17/fixed/main.go
================================================
package main

import (
	"fmt"
	"time"
)

func Work() {
	for {
		fmt.Println("Working")
		time.Sleep(1 * time.Second)
	}
}

func Send(ch chan<- int)                  { ch <- 42 }
func Recv(ch <-chan int, done chan<- int) { done <- <-ch }

func main() {
	ch, done := make(chan int), make(chan int)
	go Send(ch)
	go Recv(ch, done)
	go Work()

	<-done
}


================================================
FILE: examples/popl17/forselect/main.go
================================================
// Command nodet-for-select is a for-select pattern between two compatible
// recursive select.
package main

import "fmt"

func sel1(ch1, ch2 chan int, done chan struct{}) {
	for {
		select {
		case <-ch1:
			fmt.Println("sel1: recv")
			done <- struct{}{}
			return
		case ch2 <- 1:
			fmt.Println("sel1: send")
		}
	}
}

func sel2(ch1, ch2 chan int, done chan struct{}) {
	for {
		select {
		case <-ch2:
			fmt.Println("sel2: recv")
		case ch1 <- 2:
			fmt.Println("sel2: send")
			done <- struct{}{}
			return
		}
	}
}

func main() {
	done := make(chan struct{})
	a := make(chan int)
	b := make(chan int)
	go sel1(a, b, done)
	go sel2(a, b, done)

	<-done
	<-done
}


================================================
FILE: examples/popl17/jobsched/main.go
================================================
package main

import (
	"fmt"
	"time"
)

var i int

func worker(id int, jobQueue <-chan int, done <-chan struct{}) {
	for {
		select {
		case jobID := <-jobQueue:
			fmt.Println(id, "Executing job", jobID)
		case <-done:
			fmt.Println(id, "Quits")
			return
		}
	}
}

func morejob() bool {
	i++
	return i < 20
}

func producer(q chan int, done chan struct{}) {
	for morejob() {
		q <- 42
	}
	close(done)
}

func main() {
	jobQueue := make(chan int)
	done := make(chan struct{})
	go worker(1, jobQueue, done)
	go worker(2, jobQueue, done)
	producer(jobQueue, done)
	time.Sleep(1 * time.Second)
}


================================================
FILE: examples/popl17/mismatch/main.go
================================================
package main

import (
	"fmt"
	"time"
)

func Work() {
	for {
		fmt.Println("Working")
		time.Sleep(1 * time.Second)
	}
}

func Send(ch chan<- int)                  { ch <- 42 }
func Recv(ch <-chan int, done chan<- int) { done <- <-ch }

func main() {
	ch, done := make(chan int), make(chan int)
	go Send(ch)
	go Recv(ch, done)
	go Recv(ch, done)
	go Work()

	<-done
	<-done
}


================================================
FILE: examples/popl17/sieve/main.go
================================================
// Command infinite-primesieve is a modified primesieve example from Golang
// blog. The program generates an infinite list  (instead of a fixed number) of
// prime numbers.
//
// Original: https://golang.org/doc/play/sieve.go
//
package main

import (
	"fmt"
)

// Send the sequence 2, 3, 4, ... to channel 'ch'.
func Generate(ch chan<- int) {
	for i := 2; ; i++ {
		ch <- i // Send 'i' to channel 'ch'.
	}
}

// Copy the values from channel 'in' to channel 'out',
// removing those divisible by 'prime'.
func Filter(in <-chan int, out chan<- int, prime int) {
	for {
		i := <-in // Receive value from 'in'.
		if i%prime != 0 {
			out <- i // Send 'i' to 'out'.
		}
	}
}

// The prime sieve: Daisy-chain Filter processes.
func main() {
	ch := make(chan int) // Create a new channel.
	go Generate(ch)      // Launch Generate goroutine.
	for i := 0; ; i++ {
		prime := <-ch
		fmt.Println(prime)
		ch1 := make(chan int)
		go Filter(ch, ch1, prime)
		ch = ch1
	}
}


================================================
FILE: examples/popl17ae/close/main.go
================================================
package main

func main() {
	ch := make(chan int)
	close(ch)
}


================================================
FILE: examples/popl17ae/emptyselect/main.go
================================================
package main

func s(ch chan int) {
	ch <- 5
}

func main() {
	ch := make(chan int, 2)
	select {
	case <-ch:
	default:
	}
	s(ch)
}


================================================
FILE: examples/powsers/powser1.go
================================================
// run

// +build ignore

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Test concurrency primitives: power series.

// Power series package
// A power series is a channel, along which flow rational
// coefficients.  A denominator of zero signifies the end.
// Original code in Newsqueak by Doug McIlroy.
// See Squinting at Power Series by Doug McIlroy,
//   http://www.cs.bell-labs.com/who/rsc/thread/squint.pdf

package main

import "os"

type rat struct {
	num, den int64 // numerator, denominator
}

func (u rat) pr() {
	if u.den == 1 {
		print(u.num)
	} else {
		print(u.num, "/", u.den)
	}
	print(" ")
}

func (u rat) eq(c rat) bool {
	return u.num == c.num && u.den == c.den
}

type dch struct {
	req chan int
	dat chan rat
	nam int
}

type dch2 [2]*dch

var chnames string
var chnameserial int
var seqno int

func mkdch() *dch {
	c := chnameserial % len(chnames)
	chnameserial++
	d := new(dch)
	d.req = make(chan int)
	d.dat = make(chan rat)
	d.nam = c
	return d
}

func mkdch2() *dch2 {
	d2 := new(dch2)
	d2[0] = mkdch()
	d2[1] = mkdch()
	return d2
}

// split reads a single demand channel and replicates its
// output onto two, which may be read at different rates.
// A process is created at first demand for a rat and dies
// after the rat has been sent to both outputs.

// When multiple generations of split exist, the newest
// will service requests on one channel, which is
// always renamed to be out[0]; the oldest will service
// requests on the other channel, out[1].  All generations but the
// newest hold queued data that has already been sent to
// out[0].  When data has finally been sent to out[1],
// a signal on the release-wait channel tells the next newer
// generation to begin servicing out[1].

func dosplit(in *dch, out *dch2, wait chan int) {
	both := false // do not service both channels

	select {
	case <-out[0].req:

	case <-wait:
		both = true
		select {
		case <-out[0].req:

		case <-out[1].req:
			out[0], out[1] = out[1], out[0]
		}
	}

	seqno++
	in.req <- seqno
	release := make(chan int)
	go dosplit(in, out, release)
	dat := <-in.dat
	out[0].dat <- dat
	if !both {
		<-wait
	}
	<-out[1].req
	out[1].dat <- dat
	release <- 0
}

func split(in *dch, out *dch2) {
	release := make(chan int)
	go dosplit(in, out, release)
	release <- 0
}

func put(dat rat, out *dch) {
	<-out.req
	out.dat <- dat
}

func get(in *dch) rat {
	seqno++
	in.req <- seqno
	return <-in.dat
}

// Get one rat from each of n demand channels

func getn(in []*dch) []rat {
	n := len(in)
	if n != 2 {
		panic("bad n in getn")
	}
	req := new([2]chan int)
	dat := new([2]chan rat)
	out := make([]rat, 2)
	var i int
	var it rat
	for i = 0; i < n; i++ {
		req[i] = in[i].req
		dat[i] = nil
	}
	for n = 2 * n; n > 0; n-- {
		seqno++

		select {
		case req[0] <- seqno:
			dat[0] = in[0].dat
			req[0] = nil
		case req[1] <- seqno:
			dat[1] = in[1].dat
			req[1] = nil
		case it = <-dat[0]:
			out[0] = it
			dat[0] = nil
		case it = <-dat[1]:
			out[1] = it
			dat[1] = nil
		}
	}
	return out
}

// Get one rat from each of 2 demand channels

func get2(in0 *dch, in1 *dch) []rat {
	return getn([]*dch{in0, in1})
}

func copy(in *dch, out *dch) {
	for {
		<-out.req
		out.dat <- get(in)
	}
}

func repeat(dat rat, out *dch) {
	for {
		put(dat, out)
	}
}

type PS *dch    // power series
type PS2 *[2]PS // pair of power series

var Ones PS
var Twos PS

func mkPS() *dch {
	return mkdch()
}

func mkPS2() *dch2 {
	return mkdch2()
}

// Conventions
// Upper-case for power series.
// Lower-case for rationals.
// Input variables: U,V,...
// Output variables: ...,Y,Z

// Integer gcd; needed for rational arithmetic

func gcd(u, v int64) int64 {
	if u < 0 {
		return gcd(-u, v)
	}
	if u == 0 {
		return v
	}
	return gcd(v%u, u)
}

// Make a rational from two ints and from one int

func i2tor(u, v int64) rat {
	g := gcd(u, v)
	var r rat
	if v > 0 {
		r.num = u / g
		r.den = v / g
	} else {
		r.num = -u / g
		r.den = -v / g
	}
	return r
}

func itor(u int64) rat {
	return i2tor(u, 1)
}

var zero rat
var one rat

// End mark and end test

var finis rat

func end(u rat) int64 {
	if u.den == 0 {
		return 1
	}
	return 0
}

// Operations on rationals

func add(u, v rat) rat {
	g := gcd(u.den, v.den)
	return i2tor(u.num*(v.den/g)+v.num*(u.den/g), u.den*(v.den/g))
}

func mul(u, v rat) rat {
	g1 := gcd(u.num, v.den)
	g2 := gcd(u.den, v.num)
	var r rat
	r.num = (u.num / g1) * (v.num / g2)
	r.den = (u.den / g2) * (v.den / g1)
	return r
}

func neg(u rat) rat {
	return i2tor(-u.num, u.den)
}

func sub(u, v rat) rat {
	return add(u, neg(v))
}

func inv(u rat) rat { // invert a rat
	if u.num == 0 {
		panic("zero divide in inv")
	}
	return i2tor(u.den, u.num)
}

// print eval in floating point of PS at x=c to n terms
func evaln(c rat, U PS, n int) {
	xn := float64(1)
	x := float64(c.num) / float64(c.den)
	val := float64(0)
	for i := 0; i < n; i++ {
		u := get(U)
		if end(u) != 0 {
			break
		}
		val = val + x*float64(u.num)/float64(u.den)
		xn = xn * x
	}
	print(val, "\n")
}

// Print n terms of a power series
func printn(U PS, n int) {
	done := false
	for ; !done && n > 0; n-- {
		u := get(U)
		if end(u) != 0 {
			done = true
		} else {
			u.pr()
		}
	}
	print(("\n"))
}

// Evaluate n terms of power series U at x=c
func eval(c rat, U PS, n int) rat {
	if n == 0 {
		return zero
	}
	y := get(U)
	if end(y) != 0 {
		return zero
	}
	return add(y, mul(c, eval(c, U, n-1)))
}

// Power-series constructors return channels on which power
// series flow.  They start an encapsulated generator that
// puts the terms of the series on the channel.

// Make a pair of power series identical to a given power series

func Split(U PS) *dch2 {
	UU := mkdch2()
	go split(U, UU)
	return UU
}

// Add two power series
func Add(U, V PS) PS {
	Z := mkPS()
	go func() {
		var uv []rat
		for {
			<-Z.req
			uv = get2(U, V)
			switch end(uv[0]) + 2*end(uv[1]) {
			case 0:
				Z.dat <- add(uv[0], uv[1])
			case 1:
				Z.dat <- uv[1]
				copy(V, Z)
			case 2:
				Z.dat <- uv[0]
				copy(U, Z)
			case 3:
				Z.dat <- finis
			}
		}
	}()
	return Z
}

// Multiply a power series by a constant
func Cmul(c rat, U PS) PS {
	Z := mkPS()
	go func() {
		done := false
		for !done {
			<-Z.req
			u := get(U)
			if end(u) != 0 {
				done = true
			} else {
				Z.dat <- mul(c, u)
			}
		}
		Z.dat <- finis
	}()
	return Z
}

// Subtract

func Sub(U, V PS) PS {
	return Add(U, Cmul(neg(one), V))
}

// Multiply a power series by the monomial x^n

func Monmul(U PS, n int) PS {
	Z := mkPS()
	go func() {
		for ; n > 0; n-- {
			put(zero, Z)
		}
		copy(U, Z)
	}()
	return Z
}

// Multiply by x

func Xmul(U PS) PS {
	return Monmul(U, 1)
}

func Rep(c rat) PS {
	Z := mkPS()
	go repeat(c, Z)
	return Z
}

// Monomial c*x^n

func Mon(c rat, n int) PS {
	Z := mkPS()
	go func() {
		if c.num != 0 {
			for ; n > 0; n = n - 1 {
				put(zero, Z)
			}
			put(c, Z)
		}
		put(finis, Z)
	}()
	return Z
}

func Shift(c rat, U PS) PS {
	Z := mkPS()
	go func() {
		put(c, Z)
		copy(U, Z)
	}()
	return Z
}

// simple pole at 1: 1/(1-x) = 1 1 1 1 1 ...

// Convert array of coefficients, constant term first
// to a (finite) power series

/*
func Poly(a []rat) PS {
	Z:=mkPS()
	begin func(a []rat, Z PS) {
		j:=0
		done:=0
		for j=len(a); !done&&j>0; j=j-1)
			if(a[j-1].num!=0) done=1
		i:=0
		for(; i<j; i=i+1) put(a[i],Z)
		put(finis,Z)
	}()
	return Z
}
*/

// Multiply. The algorithm is
//	let U = u + x*UU
//	let V = v + x*VV
//	then UV = u*v + x*(u*VV+v*UU) + x*x*UU*VV

func Mul(U, V PS) PS {
	Z := mkPS()
	go func() {
		<-Z.req
		uv := get2(U, V)
		if end(uv[0]) != 0 || end(uv[1]) != 0 {
			Z.dat <- finis
		} else {
			Z.dat <- mul(uv[0], uv[1])
			UU := Split(U)
			VV := Split(V)
			W := Add(Cmul(uv[0], VV[0]), Cmul(uv[1], UU[0]))
			<-Z.req
			Z.dat <- get(W)
			copy(Add(W, Mul(UU[1], VV[1])), Z)
		}
	}()
	return Z
}

// Differentiate

func Diff(U PS) PS {
	Z := mkPS()
	go func() {
		<-Z.req
		u := get(U)
		if end(u) == 0 {
			done := false
			for i := 1; !done; i++ {
				u = get(U)
				if end(u) != 0 {
					done = true
				} else {
					Z.dat <- mul(itor(int64(i)), u)
					<-Z.req
				}
			}
		}
		Z.dat <- finis
	}()
	return Z
}

// Integrate, with const of integration
func Integ(c rat, U PS) PS {
	Z := mkPS()
	go func() {
		put(c, Z)
		done := false
		for i := 1; !done; i++ {
			<-Z.req
			u := get(U)
			if end(u) != 0 {
				done = true
			}
			Z.dat <- mul(i2tor(1, int64(i)), u)
		}
		Z.dat <- finis
	}()
	return Z
}

// Binomial theorem (1+x)^c

func Binom(c rat) PS {
	Z := mkPS()
	go func() {
		n := 1
		t := itor(1)
		for c.num != 0 {
			put(t, Z)
			t = mul(mul(t, c), i2tor(1, int64(n)))
			c = sub(c, one)
			n++
		}
		put(finis, Z)
	}()
	return Z
}

// Reciprocal of a power series
//	let U = u + x*UU
//	let Z = z + x*ZZ
//	(u+x*UU)*(z+x*ZZ) = 1
//	z = 1/u
//	u*ZZ + z*UU +x*UU*ZZ = 0
//	ZZ = -UU*(z+x*ZZ)/u

func Recip(U PS) PS {
	Z := mkPS()
	go func() {
		ZZ := mkPS2()
		<-Z.req
		z := inv(get(U))
		Z.dat <- z
		split(Mul(Cmul(neg(z), U), Shift(z, ZZ[0])), ZZ)
		copy(ZZ[1], Z)
	}()
	return Z
}

// Exponential of a power series with constant term 0
// (nonzero constant term would make nonrational coefficients)
// bug: the constant term is simply ignored
//	Z = exp(U)
//	DZ = Z*DU
//	integrate to get Z

func Exp(U PS) PS {
	ZZ := mkPS2()
	split(Integ(one, Mul(ZZ[0], Diff(U))), ZZ)
	return ZZ[1]
}

// Substitute V for x in U, where the leading term of V is zero
//	let U = u + x*UU
//	let V = v + x*VV
//	then S(U,V) = u + VV*S(V,UU)
// bug: a nonzero constant term is ignored

func Subst(U, V PS) PS {
	Z := mkPS()
	go func() {
		VV := Split(V)
		<-Z.req
		u := get(U)
		Z.dat <- u
		if end(u) == 0 {
			if end(get(VV[0])) != 0 {
				put(finis, Z)
			} else {
				copy(Mul(VV[0], Subst(U, VV[1])), Z)
			}
		}
	}()
	return Z
}

// Monomial Substition: U(c x^n)
// Each Ui is multiplied by c^i and followed by n-1 zeros

func MonSubst(U PS, c0 rat, n int) PS {
	Z := mkPS()
	go func() {
		c := one
		for {
			<-Z.req
			u := get(U)
			Z.dat <- mul(u, c)
			c = mul(c, c0)
			if end(u) != 0 {
				Z.dat <- finis
				break
			}
			for i := 1; i < n; i++ {
				<-Z.req
				Z.dat <- zero
			}
		}
	}()
	return Z
}

func Init() {
	chnameserial = -1
	seqno = 0
	chnames = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
	zero = itor(0)
	one = itor(1)
	finis = i2tor(1, 0)
	Ones = Rep(one)
	Twos = Rep(itor(2))
}

func check(U PS, c rat, count int, str string) {
	for i := 0; i < count; i++ {
		r := get(U)
		if !r.eq(c) {
			print("got: ")
			r.pr()
			print("should get ")
			c.pr()
			print("\n")
			panic(str)
		}
	}
}

const N = 10

func checka(U PS, a []rat, str string) {
	for i := 0; i < N; i++ {
		check(U, a[i], 1, str)
	}
}

func main() {
	Init()
	if len(os.Args) > 1 { // print
		print("Ones: ")
		printn(Ones, 10)
		print("Twos: ")
		printn(Twos, 10)
		print("Add: ")
		printn(Add(Ones, Twos), 10)
		print("Diff: ")
		printn(Diff(Ones), 10)
		print("Integ: ")
		printn(Integ(zero, Ones), 10)
		print("CMul: ")
		printn(Cmul(neg(one), Ones), 10)
		print("Sub: ")
		printn(Sub(Ones, Twos), 10)
		print("Mul: ")
		printn(Mul(Ones, Ones), 10)
		print("Exp: ")
		printn(Exp(Ones), 15)
		print("MonSubst: ")
		printn(MonSubst(Ones, neg(one), 2), 10)
		print("ATan: ")
		printn(Integ(zero, MonSubst(Ones, neg(one), 2)), 10)
	} else { // test
		check(Ones, one, 5, "Ones")
		check(Add(Ones, Ones), itor(2), 0, "Add Ones Ones") // 1 1 1 1 1
		check(Add(Ones, Twos), itor(3), 0, "Add Ones Twos") // 3 3 3 3 3
		a := make([]rat, N)
		d := Diff(Ones)
		for i := 0; i < N; i++ {
			a[i] = itor(int64(i + 1))
		}
		checka(d, a, "Diff") // 1 2 3 4 5
		in := Integ(zero, Ones)
		a[0] = zero // integration constant
		for i := 1; i < N; i++ {
			a[i] = i2tor(1, int64(i))
		}
		checka(in, a, "Integ")                               // 0 1 1/2 1/3 1/4 1/5
		check(Cmul(neg(one), Twos), itor(-2), 10, "CMul")    // -1 -1 -1 -1 -1
		check(Sub(Ones, Twos), itor(-1), 0, "Sub Ones Twos") // -1 -1 -1 -1 -1
		m := Mul(Ones, Ones)
		for i := 0; i < N; i++ {
			a[i] = itor(int64(i + 1))
		}
		checka(m, a, "Mul") // 1 2 3 4 5
		e := Exp(Ones)
		a[0] = itor(1)
		a[1] = itor(1)
		a[2] = i2tor(3, 2)
		a[3] = i2tor(13, 6)
		a[4] = i2tor(73, 24)
		a[5] = i2tor(167, 40)
		a[6] = i2tor(4051, 720)
		a[7] = i2tor(37633, 5040)
		a[8] = i2tor(43817, 4480)
		a[9] = i2tor(4596553, 362880)
		checka(e, a, "Exp") // 1 1 3/2 13/6 73/24
		at := Integ(zero, MonSubst(Ones, neg(one), 2))
		for c, i := 1, 0; i < N; i++ {
			if i%2 == 0 {
				a[i] = zero
			} else {
				a[i] = i2tor(int64(c), int64(i))
				c *= -1
			}
		}
		checka(at, a, "ATan") // 0 -1 0 -1/3 0 -1/5
		/*
			t := Revert(Integ(zero, MonSubst(Ones, neg(one), 2)))
			a[0] = zero
			a[1] = itor(1)
			a[2] = zero
			a[3] = i2tor(1,3)
			a[4] = zero
			a[5] = i2tor(2,15)
			a[6] = zero
			a[7] = i2tor(17,315)
			a[8] = zero
			a[9] = i2tor(62,2835)
			checka(t, a, "Tan")  // 0 1 0 1/3 0 2/15
		*/
	}
}


================================================
FILE: examples/producer-consumer/main.go
================================================
package main

// Producer-Consumer example.
// http://www.golangpatterns.info/concurrency/producer-consumer

import "fmt"

var done = make(chan bool)
var msgs = make(chan int)

func produce() {
	for i := 0; i < 10; i++ {
		msgs <- i
	}
	done <- true
}

func consume() {
	for {
		msg := <-msgs
		fmt.Println(msg)
	}
}

func main() {
	go produce()
	go consume()
	<-done
}


================================================
FILE: examples/ring-pattern/main.go
================================================
package main

import "fmt"

func numprocs() int {
	return 10
}

func adder(in <-chan int, out chan<- int) {
	for {
		out <- (<-in + 1)
	}
}

func main() {
	chOne := make(chan int)
	chOut := chOne
	chIn := chOne
	for i := 0; i < numprocs(); i++ {
		chOut = make(chan int)
		go adder(chIn, chOut)
		chIn = chOut
	}
	chOne <- 0
	fmt.Println(<-chOut)
}


================================================
FILE: examples/russ-cox-fizzbuzz/main.go
================================================
package main

import "fmt"

func main() {
	c := generate()
	c = filter(c, 3, "Fizz")
	c = filter(c, 5, "Buzz")
	for i := 1; i <= 100; i++ {
		if s := <-c; s != "" {
			fmt.Println(s)
		} else {
			fmt.Println(i)
		}
	}
}

func generate() <-chan string {
	c := make(chan string)
	go func() {
		for {
			c <- ""
		}
	}()
	return c
}

func filter(c <-chan string, n int, label string) <-chan string {
	out := make(chan string)
	go func() {
		for {
			for i := 0; i < n-1; i++ {
				out <- <-c
			}
			out <- <-c + label
		}
	}()
	return out
}



================================================
FILE: examples/select-with-continuation/main.go
================================================
package main

import (
	"fmt"
)

func main() {

	ch1 := make(chan int)
	ch2 := make(chan int)
	ch3 := make(chan int)

	select {
	case x := <-ch1:
		fmt.Println("Received x", x)
	case ch2 <- 43:
		fmt.Println("ok sent")
	case <-ch3:
	default:
		fmt.Println("asdfsdafsad")
	}
	ch1 <- 32
	fmt.Println("asdfsadfs")
}


================================================
FILE: examples/select-with-weak-mismatch/main.go
================================================
package main

// This example tests how select works. Note that ch1 is never selected.

import (
	"fmt"
)

func main() {
	ch0 := make(chan int)
	ch1 := make(chan int)

	go func() {
		ch0 <- 42
	}()

	// Blocking
	select {
	case x := <-ch0:
		fmt.Printf("Result is %d\n", x)
	case ch1 <- 2: // This is a mismatch, no receive on ch1
	}
}


================================================
FILE: examples/semaphores/main.go
================================================
package main

// Semaphores.
// Emulating semaphores with buffered channels.
// http://www.golangpatterns.info/concurrency/semaphores

type empty struct{}
type Semaphore chan empty

func NewSemaphore(cap int) Semaphore {
	return make(Semaphore, cap)
}

// acquire n resources
func (s Semaphore) P(n int) {
	e := empty{}
	for i := 0; i < n; i++ {
		s <- e
	}

}

// release n resources
func (s Semaphore) V(n int) {
	for i := 0; i < n; i++ {
		<-s
	}

}

func main() {
}


================================================
FILE: examples/send-recv-with-interfaces/main.go
================================================
package main

import (
	"time"
)

type Interacter interface {
	Send(ch chan int)
	Recv(ch chan int)
}

type S struct{}

func (st S) Send(ch chan int) {
	ch <- 42
}

func (st S) Recv(ch chan int) {
	<-ch
}

func main() {
	x := S{}
	c := make(chan int)
	go x.Send(c)
	x.Recv(c)
	time.Sleep(1 * time.Second)
}


================================================
FILE: examples/simple/main.go
================================================
package main

// Simplest way of disabling the deadlock detector.

import _ "net"

func main() {
	ch := make(chan int)
	<-ch
}


================================================
FILE: examples/single-gortn-method-call/main.go
================================================
package main

var x = 1323

type T struct {
	x int
	y chan int
}

func (t *T) setup(y int) {
	t.x = y
	t.y = make(chan int)
}

func main() {
	var t T
	x := 12
	t.setup(x)
	t.y <- 42 // Nobody to receive!
}


================================================
FILE: examples/spawn-in-choice/main.go
================================================
package main

import (
	"flag"
	"fmt"
	"os"
	"strconv"
	"time"
)

func S(out chan int) {
	out <- 42
	fmt.Println("Sent 42")
}

func R(in chan int) {
	fmt.Printf("Received %d\n", <-in)
}

// Natural branch
func main() {
	ch1 := make(chan int)
	ch2 := make(chan int)

	flag.Parse()
	fmt.Printf("NArg=%d\n", flag.NArg())
	if flag.NArg() > 0 {
		s := flag.Arg(0)
		i, err := strconv.Atoi(s)
		if err != nil {
			os.Exit(2)
		}

		if i > 0 {
			fmt.Println("Branch one")
			go R(ch2)
			go S(ch2)
		} else {
			fmt.Println("Branch two")
			go R(ch1)
			go S(ch1)
		}
		time.Sleep(1 * time.Second)
	}
}


================================================
FILE: examples/squaring-cancellation/main.go
================================================
// Command squaring-cancellation comes from Golang blog to demonstrate fan-in and
// explicit cancellation. This version is with explicit cancel (through done).
//
// Source: https://blog.golang.org/pipelines
package main

import (
	"fmt"
	"sync"
)

func gen(done <-chan struct{}, nums ...int) <-chan int {
	out := make(chan int)
	go func() {
		defer close(out)
		for _, n := range nums {
			select {
			case out <- n:
			case <-done:
				return
			}
		}
	}()
	return out
}

func sq(done <-chan struct{}, in <-chan int) <-chan int {
	out := make(chan int)
	go func() {
		defer close(out)
		for n := range in {
			select {
			case out <- n * n:
			case <-done:
				return
			}
		}
	}()
	return out
}

func merge(done <-chan struct{}, cs ...<-chan int) <-chan int {
	var wg sync.WaitGroup
	out := make(chan int)

	// Start an output goroutine for each input channel in cs.  output
	// copies values from c to out until c is closed, then calls wg.Done.
	output := func(c <-chan int) {
		defer wg.Done()
		for n := range c {
			select {
			case out <- n:
			case <-done:
				return
			}
		}
	}
	wg.Add(len(cs))
	for _, c := range cs {
		go output(c)
	}

	// Start a goroutine to close out once all the output goroutines are
	// done.  This must start after the wg.Add call.
	go func() {
		wg.Wait()
		close(out)
	}()
	return out
}

func main() {
	// Set up a done channel that's shared by the whole pipeline,
	// and close that channel when this pipeline exits, as a signal
	// for all the goroutines we started to exit.
	done := make(chan struct{})
	defer close(done)

	in := gen(done, 2, 3)

	// Distribute the sq work across two goroutines that both read from in.
	c1 := sq(done, in)
	c2 := sq(done, in)

	// Consume the first value from c1 and c2.
	out := merge(done, c1, c2)
	fmt.Println(<-out)

	// done will be closed by the deferred call.
}


================================================
FILE: examples/squaring-fanin/main.go
================================================
// Command squaring-fainin comes from Golang blog to demonstrate fan-in and
// explicit cancellation. This version uses fan-in to read all produced value.
//
// Source: https://blog.golang.org/pipelines
package main

import (
	"fmt"
	"sync"
)

// The first stage, gen, is a function that converts a list of integers to a
// channel that emits the integers in the list. The gen function starts a
// goroutine that sends the integers on the channel and closes the channel when
// all the values have been sent:
func gen(nums ...int) <-chan int {
	out := make(chan int)
	go func() {
		for _, n := range nums {
			out <- n
		}
		close(out)
	}()
	return out
}

// The second stage, sq, receives integers from a channel and returns a channel
// that emits the square of each received integer. After the inbound channel is
// closed and this stage has sent all the values downstream, it closes the
// outbound channel:
func sq(in <-chan int) <-chan int {
	out := make(chan int)
	go func() {
		for n := range in {
			out <- n * n
		}
		close(out)
	}()
	return out
}

func merge(cs ...<-chan int) <-chan int {
	var wg sync.WaitGroup
	out := make(chan int)

	// Start an output goroutine for each input channel in cs.  output
	// copies values from c to out until c is closed, then calls wg.Done.
	output := func(c <-chan int) {
		for n := range c {
			out <- n
		}
		wg.Done()
	}
	wg.Add(len(cs))
	for _, c := range cs {
		go output(c)
	}

	// Start a goroutine to close out once all the output goroutines are
	// done.  This must start after the wg.Add call.
	go func() {
		wg.Wait()
		close(out)
	}()
	return out
}

func main() {
	in := gen(2, 3)

	// Distribute the sq work across two goroutines that both read from in.
	c1 := sq(in)
	c2 := sq(in)

	// Consume the merged output from c1 and c2.
	for n := range merge(c1, c2) {
		fmt.Println(n) // 4 then 9, or 9 then 4
	}
}


================================================
FILE: examples/squaring-fanin-bad/main.go
================================================
// Command squaring-fanin-bad comes from Golang blog to demonstrate fan-in and
// explicit cancellation. This version uses fan-in but not all values are
// consumed (resources leak).
//
// Source: https://blog.golang.org/pipelines
package main

import (
	"fmt"
	"sync"
)

// The first stage, gen, is a function that converts a list of integers to a
// channel that emits the integers in the list. The gen function starts a
// goroutine that sends the integers on the channel and closes the channel when
// all the values have been sent:
func gen(nums ...int) <-chan int {
	out := make(chan int)
	go func() {
		for _, n := range nums {
			out <- n
		}
		close(out)
	}()
	return out
}

// The second stage, sq, receives integers from a channel and returns a channel
// that emits the square of each received integer. After the inbound channel is
// closed and this stage has sent all the values downstream, it closes the
// outbound channel:
func sq(in <-chan int) <-chan int {
	out := make(chan int)
	go func() {
		for n := range in {
			out <- n * n
		}
		close(out)
	}()
	return out
}

func merge(cs ...<-chan int) <-chan int {
	var wg sync.WaitGroup
	out := make(chan int)

	// Start an output goroutine for each input channel in cs.  output
	// copies values from c to out until c is closed, then calls wg.Done.
	output := func(c <-chan int) {
		for n := range c {
			out <- n
		}
		wg.Done()
	}
	wg.Add(len(cs))
	for _, c := range cs {
		go output(c)
	}

	// Start a goroutine to close out once all the output goroutines are
	// done.  This must start after the wg.Add call.
	go func() {
		wg.Wait()
		close(out)
	}()
	return out
}

func main() {
	in := gen(2, 3)

	// Distribute the sq work across two goroutines that both read from in.
	c1 := sq(in)
	c2 := sq(in)

	// Consume the first value from output
	out := merge(c1, c2)
	fmt.Println(<-out)
	return
	// Since we didn't receive the second value from out,
	// one of the output goroutines is hung attempting to send it.
}


================================================
FILE: examples/squaring-pipeline/main.go
================================================
// Command squaring-pipeline comes from Golang blog to demonstrate fan-in and
// explicit cancellation. This is a modified example of the example.
//
// Source: https://blog.golang.org/pipelines
package main

import "fmt"

// The first stage, gen, is a function that converts a list of integers to a
// channel that emits the integers in the list. The gen function starts a
// goroutine that sends the integers on the channel and closes the channel when
// all the values have been sent:
func gen(nums ...int) <-chan int {
	out := make(chan int)
	go func() {
		for _, n := range nums {
			out <- n
		}
		close(out)
	}()
	return out
}

// The second stage, sq, receives integers from a channel and returns a channel
// that emits the square of each received integer. After the inbound channel is
// closed and this stage has sent all the values downstream, it closes the
// outbound channel:
func sq(in <-chan int) <-chan int {
	out := make(chan int)
	go func() {
		for n := range in {
			out <- n * n
		}
		close(out)
	}()
	return out
}

// The main function sets up the pipeline and runs the final stage: it receives
// values from the second stage and prints each one, until the channel is
// closed:
func main() {
	// Set up the pipeline and consume the output.
	for n := range sq(sq(gen(2, 3))) {
		fmt.Println(n) // 15 then 81
	}
}


================================================
FILE: examples/struct-done-channel/main.go
================================================
package main

type T struct {
	done  chan struct{}
	value int
}

func X(ctx T) {
	ctx.done <- struct{}{}
}

func main() {
	ctx := T{
		done:  make(chan struct{}),
		value: 3,
	}
	go X(ctx)
	<-ctx.done
}


================================================
FILE: examples/timeout-behaviour/main.go
================================================
package main

import (
	"fmt"
	"time"
)

func main() {
	done := make(chan struct{})
	ch := make(chan int)
	go func(ch chan int, done chan struct{}) {
		time.Sleep(1 * time.Second)
		ch <- 42
		fmt.Println("Sent")
		done <- struct{}{}
	}(ch, done)
	select {
	case v := <-ch:
		fmt.Println("received value of", v)
	case <-time.After(1 * time.Second):
		fmt.Println("Timeout: spawn goroutine to cleanup")
		fmt.Println("value received after cleanup:", <-ch)
	case <-time.After(1 * time.Second):
		fmt.Println("Timeout2: spawn goroutine to cleanup")
		fmt.Println("value received after cleanup:", <-ch)
	}
	<-done
	fmt.Println("All Done")
}


================================================
FILE: fairness/fairness.go
================================================
// Package fairness runs a fairness analysis.
//
// Fairness analysis is an estimation of loop and recursive calls to find
// potentially unfair loop and recurse conditions.
//  - If the loop is a range slice/map expression --> usually safe (finite)
//  - If the loop is a range channel expression --> safe if channel is closed
//  - If the loop is an ordinary for-loop --> safe if
//     * loop condition is not constant or constant expression
//     * loop index is modified [in the loop body]
package fairness

import (
	"log"
	"os"

	"github.com/fatih/color"
	"github.com/nickng/dingo-hunter/logwriter"
	"github.com/nickng/dingo-hunter/ssabuilder"
	"golang.org/x/tools/go/ssa"
)

// FairnessAnalysis
type FairnessAnalysis struct {
	unsafe int
	total  int
	info   *ssabuilder.SSAInfo
	logger *log.Logger
}

// NewFairnessAnalysis starts a new analysis.
func NewFairnessAnalysis() *FairnessAnalysis {
	return &FairnessAnalysis{unsafe: 0, total: 0}
}

func (fa *FairnessAnalysis) Visit(fn *ssa.Function) {
	visitedBlk := make(map[*ssa.BasicBlock]bool)
	fa.logger.Printf("Visiting: %s", fn.String())
	for _, blk := range fn.Blocks {
		if _, visited := visitedBlk[blk]; !visited {
			visitedBlk[blk] = true
			fa.logger.Printf(" block %d %s", blk.Index, blk.Comment)
			// First consider blocks with loop initialisation blocks.
			if blk.Comment == "rangeindex.loop" {
				fa.total++
				fa.logger.Println(color.GreenString("✓ range loops are fair"))
			} else if blk.Comment == "rangechan.loop" {
				fa.total++
				hasClose := false
				for _, ch := range fa.info.FindChan(blk.Instrs[0].(*ssa.UnOp).X) {
					if ch.Type == ssabuilder.ChanClose {
						fa.logger.Println(color.GreenString("✓ found corresponding close() - channel range likely fair"))
						hasClose = true
					}
				}
				if !hasClose {
					fa.logger.Println(color.RedString("❌ range over channel w/o close() likely unfair (%s)", fa.info.FSet.Position(blk.Instrs[0].Pos())))
					fa.unsafe++
				}
			} else if blk.Comment == "for.loop" {
				fa.total++
				if fa.isLikelyUnsafe(blk) {
					fa.logger.Println(color.RedString("❌ for.loop maybe bad"))
					fa.unsafe++
				} else {
					fa.logger.Println(color.GreenString("✓ for.loop is ok"))
				}
			} else { // Normal blocks (or loops without initialisation blocks).
				if len(blk.Instrs) > 1 {
					if ifInst, ok := blk.Instrs[len(blk.Instrs)-1].(*ssa.If); ok {
						_, thenVisited := visitedBlk[ifInst.Block().Succs[0]]
						_, elseVisited := visitedBlk[ifInst.Block().Succs[1]]
						if thenVisited || elseVisited { // there is a loop!
							fa.total++
							if !fa.isCondFair(ifInst.Cond) {
								fa.logger.Println(color.YellowString("Warning: recurring block condition probably unfair"))
								fa.unsafe++
							} else {
								fa.logger.Println(color.GreenString("✓ recurring block is ok"))
							}
						}
					} else if jInst, ok := blk.Instrs[len(blk.Instrs)-1].(*ssa.Jump); ok {
						if _, visited := visitedBlk[jInst.Block().Succs[0]]; visited {
							fa.total++
							fa.unsafe++
							fa.logger.Println(color.RedString("❌ infinite loop or recurring block, probably bad (%s)", fa.info.FSet.Position(blk.Instrs[0].Pos())))
						}
					}
				}
			}
		}
	}
}

// isLikelyUnsafe checks if a given "for.loop" block has non-static index and
// non-static loop condition.
func (fa *FairnessAnalysis) isLikelyUnsafe(blk *ssa.BasicBlock) bool {
	for _, instr := range blk.Instrs {
		switch instr := instr.(type) {
		case *ssa.DebugRef:
		case *ssa.If: // Last instruction of block
			if !fa.isCondFair(instr.Cond) {
				fa.logger.Println(color.YellowString("Warning: loop condition probably unfair"))
				return true // Definitely unsafe
			}
		}
	}
	// Reaching here mean the exit cond is not func call or constant
	if fa.isIndexStatic(blk) {
		// If index is static or unchanged (i.e. while loop), that means
		//   1. The exit condition is NOT index
		//   2. The exit condition dependent on 'outside' variable
		// TODO(nickng): check that if-condition is used in body
		fa.logger.Println(color.YellowString("Warning: cannot find loop index"))
		return true // Assume unsafe
	}
	return false
}

// isCondFair returns true if an if condition (bool expression) is constant.
func (fa *FairnessAnalysis) isCondFair(cond ssa.Value) bool {
	switch cond := cond.(type) {
	case *ssa.Const:
		fa.logger.Println(color.YellowString("Warning: loop condition is constant"))
		return false
	case *ssa.BinOp: // <, <=, !=, ==
		if _, xConst := cond.X.(*ssa.Const); xConst {
			if _, yConst := cond.Y.(*ssa.Const); yConst {
				fa.logger.Println(color.YellowString("Warning: loop condition is constant"))
				return false
			} else {
				fa.logger.Println(color.YellowString("Try to trace back on Y"))
			}
		} else {
			fa.logger.Println(color.YellowString("Try to trace back on X"))
		}
	case *ssa.UnOp:
		if _, con := cond.X.(*ssa.Const); con {
			fa.logger.Println(color.YellowString("Warning: loop condition is constant"))
			return false
		}
	case *ssa.Call:
		fa.logger.Println(color.YellowString("Warning:%s: condition is function call --> unsure", fa.info.FSet.Position(cond.Pos()).String()))
		return false
	}
	return true // Assume fair by default
}

// isIndexStatic returns true if a block does not have a modify-index Phi.
func (fa *FairnessAnalysis) isIndexStatic(blk *ssa.BasicBlock) bool {
	for _, instr := range blk.Instrs {
		switch instr := instr.(type) {
		case *ssa.DebugRef:
		case *ssa.Phi:
			if len(instr.Comment) > 0 {
				fa.logger.Println(color.BlueString("  note: Index var %s", instr.Comment))
				return false
			}
		}
	}
	fa.logger.Println(color.BlueString("  note: Index var not found"))
	return true
}

// Check for fairness on a built SSA
func Check(info *ssabuilder.SSAInfo) {
	if cgRoot := info.CallGraph(); cgRoot != nil {
		fa := NewFairnessAnalysis()
		fa.info = info
		fa.logger = log.New(logwriter.New(os.Stdout, true, true), "fairness: ", log.LstdFlags)
		cgRoot.Traverse(fa)
		if fa.unsafe <= 0 {
			fa.logger.Printf(color.GreenString("Result: %d/%d is likely unsafe", fa.unsafe, fa.total))
		} else {
			fa.logger.Printf(color.RedString("Result: %d/%d is likely unsafe", fa.unsafe, fa.total))
		}
	}
}


================================================
FILE: go.mod
================================================
module github.com/nickng/dingo-hunter

require (
	github.com/awalterschulze/gographviz v0.0.0-20181013152038-b2885df04310
	github.com/fatih/color v1.7.0
	github.com/nickng/cfsm v1.0.0
	github.com/nickng/migo/v3 v3.0.0
	github.com/spf13/cobra v0.0.3
	github.com/spf13/viper v1.3.1
	golang.org/x/tools v0.1.12
)

require (
	github.com/BurntSushi/toml v0.3.1 // indirect
	github.com/fsnotify/fsnotify v1.4.7 // indirect
	github.com/hashicorp/hcl v1.0.0 // indirect
	github.com/inconshreveable/mousetrap v1.0.0 // indirect
	github.com/magiconair/properties v1.8.0 // indirect
	github.com/mattn/go-colorable v0.0.9 // indirect
	github.com/mattn/go-isatty v0.0.4 // indirect
	github.com/mitchellh/mapstructure v1.1.2 // indirect
	github.com/pelletier/go-toml v1.2.0 // indirect
	github.com/spf13/afero v1.1.2 // indirect
	github.com/spf13/cast v1.3.0 // indirect
	github.com/spf13/jwalterweatherman v1.0.0 // indirect
	github.com/spf13/pflag v1.0.3 // indirect
	golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
	golang.org/x/net v0.7.0 // indirect
	golang.org/x/sys v0.5.0 // indirect
	golang.org/x/text v0.7.0 // indirect
	gopkg.in/yaml.v2 v2.2.2 // indirect
)


================================================
FILE: go.sum
================================================
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/awalterschulze/gographviz v0.0.0-20181013152038-b2885df04310 h1:t+qxRrRtwNiUYA+Xh2jSXhoG2grnMCMKX4Fg6lx9X1U=
github.com/awalterschulze/gographviz v0.0.0-20181013152038-b2885df04310/go.mod h1:GEV5wmg4YquNw7v1kkyoX9etIk8yVmXj+AkDHuuETHs=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/nickng/cfsm v1.0.0 h1:Vmn8mSG7pDyhkdVqgQiMXwn3nJG98qDCSbYUKiT9IOg=
github.com/nickng/cfsm v1.0.0/go.mod h1:PrQ7n3oDClsXqcGSgpTchal04ATQHQ8QC4NfdB6DzVk=
github.com/nickng/migo/v3 v3.0.0 h1:ajAyfE+iscsoJuBB5x1CzoiHRLSacIo5Dpc7wYeEWbg=
github.com/nickng/migo/v3 v3.0.0/go.mod h1:f/kWQQkC/OszsYKduE4dffcTsQZgAkCmn41wTlPe6h8=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.3.1 h1:5+8j8FTpnFV4nEImW/ofkzEt8VoOiLXxdYIDsB73T38=
github.com/spf13/viper v1.3.1/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=


================================================
FILE: logwriter/logwriter.go
================================================
// Package logwriter wraps a io.Writer for dingo-hunter logging.
//
package logwriter // "github.com/nickng/dingo-hunter/logwriter"

import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"

	"github.com/fatih/color"
)

// Writer is a log writer and its configurations.
type Writer struct {
	io.Writer

	LogFile       string
	EnableLogging bool
	EnableColour  bool
	Cleanup       func()
}

// New creates a new file writer.
func NewFile(logfile string, enableLogging, enableColour bool) *Writer {
	return &Writer{
		LogFile:       logfile,
		EnableLogging: enableLogging,
		EnableColour:  enableColour,
	}
}

// New creates a new log writer.
func New(w io.Writer, enableLogging, enableColour bool) *Writer {
	return &Writer{
		Writer:        w,
		EnableLogging: enableLogging,
		EnableColour:  enableColour,
	}
}

// Create initialises a new writer.
func (w *Writer) Create() error {
	color.NoColor = !w.EnableColour
	if !w.EnableLogging {
		w.Writer = ioutil.Discard
		w.Cleanup = func() {}
		return nil
	}
	if w.Writer != nil {
		w.Cleanup = func() {}
		return nil
	}
	if w.LogFile != "" {
		if f, err := os.Create(w.LogFile); err != nil {
			return fmt.Errorf("Failed to create log file: %s", err)
		} else {
			bufWriter := bufio.NewWriter(f)
			w.Writer = bufWriter
			w.Cleanup = func() {
				if err := bufWriter.Flush(); err != nil {
					log.Printf("flush: %s", err)
				}
				if err := f.Close(); err != nil {
					log.Printf("close: %s", err)
				}
			}
		}
	} else { // Logfile non-empty
		w.Writer = os.Stdout
		w.Cleanup = func() {}
	}
	return nil
}


================================================
FILE: main.go
================================================
// +build go1.7

package main

import (
	"fmt"
	"os"

	"github.com/nickng/dingo-hunter/cmd"
)

func main() {
	if err := cmd.RootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}
}


================================================
FILE: migoextract/call.go
================================================
package migoextract

// Functions for handling function call-like instructions
// i.e. builtin, call, closure, defer, go.

import (
	"go/types"

	"github.com/nickng/migo/v3"
	"golang.org/x/tools/go/ssa"
)

// Call performs call on a given unprepared call context.
func (caller *Function) Call(call *ssa.Call, infer *TypeInfer, b *Block, l *Loop) {
	if call == nil {
		infer.Logger.Fatal("Call is nil")
		return
	}
	common := call.Common()
	switch fn := common.Value.(type) {
	case *ssa.Builtin:
		switch fn.Name() {
		case "close":
			ch, ok := caller.locals[common.Args[0]]
			if !ok {
				infer.Logger.Fatalf("call close: %s: %s", common.Args[0].Name(), ErrUnknownValue)
				return
			}
			if paramName, ok := caller.revlookup[ch.String()]; ok {
				caller.FuncDef.AddStmts(&migo.CloseStatement{Chan: paramName})
			} else {
				if _, ok := common.Args[0].(*ssa.Phi); ok {
					caller.FuncDef.AddStmts(&migo.CloseStatement{Chan: common.Args[0].Name()})
				} else {
					caller.FuncDef.AddStmts(&migo.CloseStatement{Chan: ch.(*Value).Name()})
				}
			}
			infer.Logger.Print(caller.Sprintf("close %s", common.Args[0]))
			return
		case "len":
			if l.State == Enter {
				len, err := caller.callLen(common, infer)
				if err == ErrRuntimeLen {
					l.Bound = Dynamic
					return
				}
				l.Bound, l.End = Static,
Download .txt
gitextract_zp5j09dq/

├── .gitignore
├── .gitmodules
├── .travis.yml
├── LICENSE
├── README.md
├── cfsmextract/
│   ├── cfsmextract.go
│   ├── func.go
│   ├── gortn.go
│   ├── sesstype/
│   │   ├── cfsm.go
│   │   ├── dot.go
│   │   ├── nodes.go
│   │   ├── nodestack.go
│   │   ├── nodestack_test.go
│   │   ├── op_string.go
│   │   ├── sesstype.go
│   │   └── sesstype_test.go
│   ├── utils/
│   │   ├── defs.go
│   │   └── emptyvalue.go
│   ├── utils.go
│   └── visit.go
├── cmd/
│   ├── buildssa.go
│   ├── cfsms.go
│   ├── checkfair.go
│   ├── migo.go
│   ├── root.go
│   └── serve.go
├── doc.go
├── examples/
│   ├── altbit/
│   │   └── main.go
│   ├── branch-dependent-deadlock/
│   │   └── main.go
│   ├── channel-scoping-test/
│   │   └── main.go
│   ├── commaok/
│   │   └── main.go
│   ├── cond-recur/
│   │   └── main.go
│   ├── deadlocking-philosophers/
│   │   └── main.go
│   ├── dining-philosophers/
│   │   └── main.go
│   ├── factorial/
│   │   └── main.go
│   ├── fanin-pattern/
│   │   └── main.go
│   ├── fanin-pattern-commaok/
│   │   └── main.go
│   ├── fcall/
│   │   └── main.go
│   ├── forselect/
│   │   └── main.go
│   ├── giachino-concur14-dining-philosopher/
│   │   └── main.go
│   ├── giachino-concur14-factorial/
│   │   └── main.go
│   ├── github-golang-go-issue-12734/
│   │   └── main.go
│   ├── golang-blog-prime-sieve/
│   │   └── main.go
│   ├── infinite-prime-sieve/
│   │   └── main.go
│   ├── issue-10-close-wrong-migo-chan-name/
│   │   └── main.go
│   ├── issue-11-non-communicating-fn-call/
│   │   └── main.go
│   ├── jobsched/
│   │   └── main.go
│   ├── local-deadlock/
│   │   └── main.go
│   ├── local-deadlock-fixed/
│   │   └── main.go
│   ├── loop-variations/
│   │   └── main.go
│   ├── makechan-in-loop/
│   │   └── main.go
│   ├── md5/
│   │   └── main.go
│   ├── multi-makechan-same-var/
│   │   └── main.go
│   ├── multiple-files/
│   │   ├── main.go
│   │   └── x.go
│   ├── multiple-timeout/
│   │   └── main.go
│   ├── parallel-buffered-recursive-fibonacci/
│   │   └── main.go
│   ├── parallel-recursive-fibonacci/
│   │   └── main.go
│   ├── parallel-twoprocess-fibonacci/
│   │   └── main.go
│   ├── philo/
│   │   └── main.go
│   ├── popl17/
│   │   ├── alt-bit/
│   │   │   └── main.go
│   │   ├── concsys/
│   │   │   └── main.go
│   │   ├── cond-recur/
│   │   │   └── main.go
│   │   ├── dinephil/
│   │   │   └── main.go
│   │   ├── fact/
│   │   │   └── main.go
│   │   ├── fanin/
│   │   │   └── main.go
│   │   ├── fanin-alt/
│   │   │   └── main.go
│   │   ├── fib/
│   │   │   └── main.go
│   │   ├── fib-async/
│   │   │   └── main.go
│   │   ├── fixed/
│   │   │   └── main.go
│   │   ├── forselect/
│   │   │   └── main.go
│   │   ├── jobsched/
│   │   │   └── main.go
│   │   ├── mismatch/
│   │   │   └── main.go
│   │   └── sieve/
│   │       └── main.go
│   ├── popl17ae/
│   │   ├── close/
│   │   │   └── main.go
│   │   └── emptyselect/
│   │       └── main.go
│   ├── powsers/
│   │   └── powser1.go
│   ├── producer-consumer/
│   │   └── main.go
│   ├── ring-pattern/
│   │   └── main.go
│   ├── russ-cox-fizzbuzz/
│   │   └── main.go
│   ├── select-with-continuation/
│   │   └── main.go
│   ├── select-with-weak-mismatch/
│   │   └── main.go
│   ├── semaphores/
│   │   └── main.go
│   ├── send-recv-with-interfaces/
│   │   └── main.go
│   ├── simple/
│   │   └── main.go
│   ├── single-gortn-method-call/
│   │   └── main.go
│   ├── spawn-in-choice/
│   │   └── main.go
│   ├── squaring-cancellation/
│   │   └── main.go
│   ├── squaring-fanin/
│   │   └── main.go
│   ├── squaring-fanin-bad/
│   │   └── main.go
│   ├── squaring-pipeline/
│   │   └── main.go
│   ├── struct-done-channel/
│   │   └── main.go
│   └── timeout-behaviour/
│       └── main.go
├── fairness/
│   └── fairness.go
├── go.mod
├── go.sum
├── logwriter/
│   └── logwriter.go
├── main.go
├── migoextract/
│   ├── call.go
│   ├── chan.go
│   ├── closure.go
│   ├── commaok.go
│   ├── context.go
│   ├── datastructure.go
│   ├── error.go
│   ├── instance.go
│   ├── loop.go
│   ├── loopbound_string.go
│   ├── loopstack.go
│   ├── loopstate_string.go
│   ├── migoextract.go
│   ├── phi.go
│   ├── pointer.go
│   ├── print.go
│   ├── select.go
│   ├── storage.go
│   ├── tuple.go
│   └── visit.go
├── ssabuilder/
│   ├── callgraph/
│   │   └── callgraph.go
│   ├── channel.go
│   ├── errors.go
│   ├── pointer.go
│   ├── print.go
│   ├── ssabuild.go
│   └── util.go
├── static/
│   ├── script.js
│   └── style.css
├── talks/
│   ├── basic/
│   │   ├── concurrency.go
│   │   ├── fanin.go
│   │   └── select.go
│   ├── conf-cc-2016/
│   │   ├── deadlock-global.html
│   │   ├── fanin-global.html
│   │   ├── select.html
│   │   ├── ssa.html
│   │   └── type-inference.html
│   ├── conf-cc-2016.slide
│   ├── deadlock/
│   │   └── deadlock.go
│   ├── snippet/
│   │   └── proc.txt
│   ├── static/
│   │   ├── article.css
│   │   ├── dir.css
│   │   ├── dir.js
│   │   ├── jquery-ui.js
│   │   ├── slides.js
│   │   └── styles.css
│   ├── talk-golanguk-2016/
│   │   ├── calculi.html
│   │   ├── compat.html
│   │   ├── datatype.go
│   │   ├── deadlock-notdetected.go
│   │   ├── deadlock.go
│   │   ├── main-type.html
│   │   ├── proc.txt
│   │   ├── receiver-type.html
│   │   ├── sender-type.html
│   │   ├── video.html
│   │   └── work-type.html
│   ├── talk-golanguk-2016.slide
│   ├── talk-jan-2016.slide
│   └── templates/
│       ├── action.tmpl
│       ├── article.tmpl
│       ├── dir.tmpl
│       └── slides.tmpl
├── templates/
│   └── index.tmpl
└── webservice/
    ├── cfsm.go
    ├── errors.go
    ├── gong.go
    ├── handlers.go
    ├── migo.go
    ├── play.go
    ├── server.go
    ├── ssa.go
    └── synthesis.go
Download .txt
SYMBOL INDEX (736 symbols across 135 files)

FILE: cfsmextract/cfsmextract.go
  type CFSMExtract (line 20) | type CFSMExtract struct
    method Run (line 47) | func (extract *CFSMExtract) Run() {
    method Session (line 108) | func (extract *CFSMExtract) Session() *sesstype.Session {
    method WriteOutput (line 112) | func (extract *CFSMExtract) WriteOutput() {
  function New (line 32) | func New(ssainfo *ssabuilder.SSAInfo, prefix, outdir string) *CFSMExtract {

FILE: cfsmextract/func.go
  type VarKind (line 14) | type VarKind
  constant Nothing (line 18) | Nothing     VarKind = iota
  constant Array (line 19) | Array
  constant Chan (line 20) | Chan
  constant Struct (line 21) | Struct
  constant LocalArray (line 22) | LocalArray
  constant LocalStruct (line 23) | LocalStruct
  constant Untracked (line 24) | Untracked
  type Captures (line 28) | type Captures
  type Tuples (line 31) | type Tuples
  type Elems (line 34) | type Elems
  type Fields (line 37) | type Fields
  type frame (line 40) | type frame struct
    method callBuiltin (line 130) | func (caller *frame) callBuiltin(common *ssa.CallCommon) {
    method call (line 156) | func (caller *frame) call(c *ssa.Call) {
    method callCommon (line 160) | func (caller *frame) callCommon(call *ssa.Call, common *ssa.CallCommon) {
    method callGo (line 276) | func (caller *frame) callGo(g *ssa.Go) {
    method translate (line 310) | func (callee *frame) translate(common *ssa.CallCommon) {
    method handleRetvals (line 355) | func (caller *frame) handleRetvals(returned ssa.Value, callee *frame) {
    method get (line 367) | func (callee *frame) get(v ssa.Value) (*utils.Definition, VarKind) {
    method handleExtRetvals (line 398) | func (caller *frame) handleExtRetvals(returned ssa.Value, callee *fram...
    method isRecursive (line 419) | func (callee *frame) isRecursive() bool {
    method printCallStack (line 444) | func (callee *frame) printCallStack() {
    method updateDefs (line 452) | func (callee *frame) updateDefs(vdOld, vdNew *utils.Definition) {
  type environ (line 56) | type environ struct
    method GetSessionChan (line 78) | func (env *environ) GetSessionChan(vd *utils.Definition) *sesstype.Chan {
  function makeToplevelFrame (line 85) | func makeToplevelFrame(extract *CFSMExtract) *frame {
  function findMethod (line 269) | func findMethod(prog *ssa.Program, meth *types.Func, typ types.Type) *ss...

FILE: cfsmextract/gortn.go
  type goroutine (line 8) | type goroutine struct
    method AddNode (line 16) | func (gortn *goroutine) AddNode(node sesstype.Node) {

FILE: cfsmextract/sesstype/cfsm.go
  constant STOP (line 12) | STOP = "STOP"
  type CFSMs (line 15) | type CFSMs struct
    method WriteTo (line 52) | func (sys *CFSMs) WriteTo(w io.Writer) (int64, error) {
    method PrintSummary (line 58) | func (sys *CFSMs) PrintSummary() {
    method rootToMachine (line 69) | func (sys *CFSMs) rootToMachine(role Role, root Node, m *cfsm.CFSM) {
    method nodeToMachine (line 75) | func (sys *CFSMs) nodeToMachine(role Role, node Node, q0 *cfsm.State, ...
    method chanToMachine (line 152) | func (sys *CFSMs) chanToMachine(ch Role, T string, m *cfsm.CFSM) {
    method isSelfLoop (line 187) | func (sys *CFSMs) isSelfLoop(m *cfsm.CFSM, q0 *cfsm.State, node Node) ...
  function NewCFSMs (line 22) | func NewCFSMs(s *Session) *CFSMs {

FILE: cfsmextract/sesstype/dot.go
  type GraphvizDot (line 12) | type GraphvizDot struct
    method WriteTo (line 39) | func (dot *GraphvizDot) WriteTo(w io.Writer) (int64, error) {
    method nodeToDotNode (line 44) | func (dot *GraphvizDot) nodeToDotNode(node Node) *gographviz.Node {
    method visitNode (line 141) | func (dot *GraphvizDot) visitNode(node Node, subgraph *gographviz.SubG...
  function NewGraphvizDot (line 19) | func NewGraphvizDot(s *Session) *GraphvizDot {

FILE: cfsmextract/sesstype/nodes.go
  function CountNodes (line 7) | func CountNodes(root Node) int {
  function SessionCountNodes (line 15) | func SessionCountNodes(session *Session) map[string]int {
  function PrintNodeSummary (line 23) | func PrintNodeSummary(session *Session) {

FILE: cfsmextract/sesstype/nodestack.go
  type NodeStack (line 4) | type NodeStack struct
    method Push (line 10) | func (s *NodeStack) Push(node Node) {
    method Pop (line 16) | func (s *NodeStack) Pop() {
    method Top (line 24) | func (s *NodeStack) Top() Node {
    method Size (line 32) | func (s *NodeStack) Size() int {
    method String (line 37) | func (s *NodeStack) String() string {
  function NewNodeStack (line 50) | func NewNodeStack() *NodeStack {

FILE: cfsmextract/sesstype/nodestack_test.go
  function TestNewStack (line 7) | func TestNewStack(t *testing.T) {

FILE: cfsmextract/sesstype/op_string.go
  constant _op_name (line 7) | _op_name = "NoOpNewChanOpSendOpRecvOpEndOp"
  method String (line 11) | func (i op) String() string {

FILE: cfsmextract/sesstype/sesstype.go
  type op (line 16) | type op
  type Chan (line 19) | type Chan struct
    method Name (line 26) | func (ch Chan) Name() string {
    method Type (line 35) | func (ch Chan) Type() types.Type {
    method Role (line 41) | func (ch Chan) Role() Role       { return ch.role }
    method Value (line 42) | func (ch Chan) Value() ssa.Value { return ch.def.Var }
  type Role (line 45) | type Role interface
  type role (line 49) | type role struct
    method Name (line 53) | func (r *role) Name() string { return r.name }
  constant NoOp (line 57) | NoOp op = iota
  constant NewChanOp (line 58) | NewChanOp
  constant SendOp (line 59) | SendOp
  constant RecvOp (line 60) | RecvOp
  constant EndOp (line 61) | EndOp
  type Node (line 65) | type Node interface
  type Session (line 75) | type Session struct
    method GetRole (line 91) | func (s *Session) GetRole(name string) Role { // Get or create role
    method MakeChan (line 99) | func (s *Session) MakeChan(v *utils.Definition, r Role) Chan {
    method MakeExtChan (line 109) | func (s *Session) MakeExtChan(v *utils.Definition, r Role) Chan {
    method String (line 341) | func (s *Session) String() string {
  function CreateSession (line 82) | func CreateSession() *Session {
  type NewChanNode (line 119) | type NewChanNode struct
    method Kind (line 124) | func (nc *NewChanNode) Kind() op   { return NewChanOp }
    method Chan (line 125) | func (nc *NewChanNode) Chan() Chan { return nc.ch }
    method Append (line 126) | func (nc *NewChanNode) Append(n Node) Node {
    method Child (line 130) | func (nc *NewChanNode) Child(i int) Node { return nc.children[i] }
    method Children (line 131) | func (nc *NewChanNode) Children() []Node { return nc.children }
    method String (line 132) | func (nc *NewChanNode) String() string {
  type SendNode (line 137) | type SendNode struct
    method Kind (line 145) | func (s *SendNode) Kind() op       { return SendOp }
    method Sender (line 146) | func (s *SendNode) Sender() Role   { return s.sndr }
    method To (line 147) | func (s *SendNode) To() Chan       { return s.dest }
    method IsNondet (line 148) | func (s *SendNode) IsNondet() bool { return s.nondet }
    method Append (line 149) | func (s *SendNode) Append(n Node) Node {
    method Child (line 153) | func (s *SendNode) Child(i int) Node { return s.children[i] }
    method Children (line 154) | func (s *SendNode) Children() []Node { return s.children }
    method String (line 155) | func (s *SendNode) String() string {
  type RecvNode (line 164) | type RecvNode struct
    method Kind (line 173) | func (r *RecvNode) Kind() op       { return RecvOp }
    method Receiver (line 174) | func (r *RecvNode) Receiver() Role { return r.rcvr }
    method From (line 175) | func (r *RecvNode) From() Chan     { return r.orig }
    method IsNondet (line 176) | func (r *RecvNode) IsNondet() bool { return r.nondet }
    method Stop (line 177) | func (r *RecvNode) Stop() bool     { return r.stop }
    method Append (line 178) | func (r *RecvNode) Append(node Node) Node {
    method Child (line 182) | func (r *RecvNode) Child(index int) Node { return r.children[index] }
    method Children (line 183) | func (r *RecvNode) Children() []Node     { return r.children }
    method String (line 184) | func (r *RecvNode) String() string {
  type LabelNode (line 196) | type LabelNode struct
    method Kind (line 201) | func (l *LabelNode) Kind() op     { return NoOp }
    method Name (line 202) | func (l *LabelNode) Name() string { return l.name }
    method Append (line 203) | func (l *LabelNode) Append(n Node) Node {
    method Child (line 207) | func (l *LabelNode) Child(i int) Node { return l.children[i] }
    method Children (line 208) | func (l *LabelNode) Children() []Node { return l.children }
    method String (line 209) | func (l *LabelNode) String() string   { return fmt.Sprintf("%s", l.nam...
  type GotoNode (line 212) | type GotoNode struct
    method Kind (line 217) | func (g *GotoNode) Kind() op     { return NoOp }
    method Name (line 218) | func (g *GotoNode) Name() string { return g.name }
    method Append (line 219) | func (g *GotoNode) Append(n Node) Node {
    method Child (line 223) | func (g *GotoNode) Child(i int) Node { return g.children[i] }
    method Children (line 224) | func (g *GotoNode) Children() []Node { return g.children }
    method String (line 225) | func (g *GotoNode) String() string   { return fmt.Sprintf("Goto %s", g...
  type EndNode (line 227) | type EndNode struct
    method Kind (line 232) | func (e *EndNode) Kind() op   { return EndOp }
    method Chan (line 233) | func (e *EndNode) Chan() Chan { return e.ch }
    method Append (line 234) | func (e *EndNode) Append(n Node) Node {
    method Child (line 238) | func (e *EndNode) Child(i int) Node { return e.children[i] }
    method Children (line 239) | func (e *EndNode) Children() []Node { return e.children }
    method String (line 240) | func (e *EndNode) String() string   { return fmt.Sprintf("End %s", e.c...
  type EmptyBodyNode (line 242) | type EmptyBodyNode struct
    method Kind (line 246) | func (e *EmptyBodyNode) Kind() op { return NoOp }
    method Append (line 247) | func (e *EmptyBodyNode) Append(node Node) Node {
    method Child (line 251) | func (e *EmptyBodyNode) Child(i int) Node { return e.children[i] }
    method Children (line 252) | func (e *EmptyBodyNode) Children() []Node { return e.children }
    method String (line 253) | func (e *EmptyBodyNode) String() string   { return "(Empty)" }
  function NewNewChanNode (line 256) | func NewNewChanNode(ch Chan) Node {
  function NewSendNode (line 261) | func NewSendNode(sndr Role, dest Chan, typ types.Type) Node {
  function NewSelectSendNode (line 272) | func NewSelectSendNode(sndr Role, dest Chan, typ types.Type) Node {
  function NewRecvNode (line 283) | func NewRecvNode(orig Chan, rcvr Role, typ types.Type) Node {
  function NewRecvStopNode (line 295) | func NewRecvStopNode(orig Chan, rcvr Role, typ types.Type) Node {
  function NewSelectRecvNode (line 307) | func NewSelectRecvNode(orig Chan, rcvr Role, typ types.Type) Node {
  function NewLabelNode (line 318) | func NewLabelNode(name string) Node {
  function NewGotoNode (line 326) | func NewGotoNode(name string) Node {
  function NewEndNode (line 334) | func NewEndNode(ch Chan) Node {
  function StringRecursive (line 358) | func StringRecursive(node Node) string {

FILE: cfsmextract/sesstype/sesstype_test.go
  function TestSendNode (line 14) | func TestSendNode(t *testing.T) {
  function TestRecvNode (line 50) | func TestRecvNode(t *testing.T) {
  function TestLabelGotoNode (line 86) | func TestLabelGotoNode(t *testing.T) {
  function TestNewChanNode (line 112) | func TestNewChanNode(t *testing.T) {
  function TestEndNode (line 133) | func TestEndNode(t *testing.T) {
  type mockChan (line 153) | type mockChan struct
    method Name (line 155) | func (mc mockChan) Name() string                  { return "MockChan" }
    method String (line 156) | func (mc mockChan) String() string                { return "Mock Chan" }
    method Type (line 157) | func (mc mockChan) Type() types.Type              { return types.NewCh...
    method Parent (line 158) | func (mc mockChan) Parent() *ssa.Function         { return nil }
    method Referrers (line 159) | func (mc mockChan) Referrers() *[]ssa.Instruction { return nil }
    method Pos (line 160) | func (mc mockChan) Pos() token.Pos                { return token.NoPos }
  function TestSelfLoop (line 162) | func TestSelfLoop(t *testing.T) {

FILE: cfsmextract/utils.go
  function loc (line 13) | func loc(fr *frame, pos token.Pos) string {
  function red (line 23) | func red(s string) string {
  function orange (line 27) | func orange(s string) string {
  function green (line 31) | func green(s string) string {
  function cyan (line 35) | func cyan(s string) string {
  function reg (line 39) | func reg(reg ssa.Value) string {
  function deref (line 49) | func deref(typ types.Type) types.Type {
  function derefAll (line 56) | func derefAll(typ types.Type) types.Type {

FILE: cfsmextract/utils/defs.go
  type Definition (line 14) | type Definition struct
    method String (line 38) | func (vd *Definition) String() string {
  function NewDef (line 20) | func NewDef(v ssa.Value) *Definition {

FILE: cfsmextract/utils/emptyvalue.go
  type EmptyValue (line 15) | type EmptyValue struct
    method Name (line 19) | func (v EmptyValue) Name() string                  { return "(Nothingn...
    method String (line 20) | func (v EmptyValue) String() string                { return "(Empty Va...
    method Type (line 21) | func (v EmptyValue) Type() types.Type              { return v.T }
    method Parent (line 22) | func (v EmptyValue) Parent() *ssa.Function         { return nil }
    method Referrers (line 23) | func (v EmptyValue) Referrers() *[]ssa.Instruction { return nil }
    method Pos (line 24) | func (v EmptyValue) Pos() token.Pos                { return token.NoPos }

FILE: cfsmextract/visit.go
  function visitBlock (line 14) | func visitBlock(blk *ssa.BasicBlock, fr *frame) {
  function visitFunc (line 35) | func visitFunc(fn *ssa.Function, callee *frame) bool {
  function visitInst (line 45) | func visitInst(inst ssa.Instruction, fr *frame) {
  function visitExtract (line 147) | func visitExtract(e *ssa.Extract, fr *frame) {
  function visitMakeClosure (line 198) | func visitMakeClosure(inst *ssa.MakeClosure, fr *frame) {
  function visitAlloc (line 207) | func visitAlloc(inst *ssa.Alloc, fr *frame) {
  function visitDeref (line 247) | func visitDeref(inst *ssa.UnOp, fr *frame) {
  function visitSelect (line 293) | func visitSelect(s *ssa.Select, fr *frame) {
  function visitReturn (line 342) | func visitReturn(ret *ssa.Return, fr *frame) []*utils.Definition {
  function visitCall (line 352) | func visitCall(c *ssa.Call, caller *frame) {
  function visitIf (line 356) | func visitIf(inst *ssa.If, fr *frame) {
  function visitMakeChan (line 411) | func visitMakeChan(inst *ssa.MakeChan, caller *frame) {
  function visitSend (line 425) | func visitSend(send *ssa.Send, fr *frame) {
  function visitRecv (line 444) | func visitRecv(recv *ssa.UnOp, fr *frame) {
  function visitClose (line 471) | func visitClose(ch sesstype.Chan, fr *frame) {
  function visitJump (line 475) | func visitJump(inst *ssa.Jump, fr *frame) {
  function visitStore (line 483) | func visitStore(inst *ssa.Store, fr *frame) {
  function visitChangeType (line 550) | func visitChangeType(inst *ssa.ChangeType, fr *frame) {
  function visitChangeInterface (line 568) | func visitChangeInterface(inst *ssa.ChangeInterface, fr *frame) {
  function visitBinOp (line 573) | func visitBinOp(inst *ssa.BinOp, fr *frame) {
  function visitMakeInterface (line 592) | func visitMakeInterface(inst *ssa.MakeInterface, fr *frame) {
  function visitSlice (line 607) | func visitSlice(inst *ssa.Slice, fr *frame) {
  function visitMakeSlice (line 611) | func visitMakeSlice(inst *ssa.MakeSlice, fr *frame) {
  function visitFieldAddr (line 615) | func visitFieldAddr(inst *ssa.FieldAddr, fr *frame) {
  function visitField (line 679) | func visitField(inst *ssa.Field, fr *frame) {
  function visitIndexAddr (line 743) | func visitIndexAddr(inst *ssa.IndexAddr, fr *frame) {
  function visitIndex (line 794) | func visitIndex(inst *ssa.Index, fr *frame) {
  function visitDefer (line 845) | func visitDefer(inst *ssa.Defer, fr *frame) {
  function visitRunDefers (line 849) | func visitRunDefers(inst *ssa.RunDefers, fr *frame) {
  function visitPhi (line 855) | func visitPhi(inst *ssa.Phi, fr *frame) {
  function visitTypeAssert (line 864) | func visitTypeAssert(inst *ssa.TypeAssert, fr *frame) {

FILE: cmd/buildssa.go
  function init (line 41) | func init() {
  function Build (line 51) | func Build(files []string) {

FILE: cmd/cfsms.go
  function init (line 44) | func init() {
  function extractCFSMs (line 51) | func extractCFSMs(files []string) {

FILE: cmd/checkfair.go
  function init (line 39) | func init() {
  function check (line 43) | func check(files []string) {

FILE: cmd/migo.go
  function init (line 45) | func init() {
  function extractMigo (line 51) | func extractMigo(files []string) {

FILE: cmd/root.go
  function Execute (line 44) | func Execute() {
  function init (line 51) | func init() {
  function initConfig (line 61) | func initConfig() {

FILE: cmd/serve.go
  constant basePkg (line 40) | basePkg = "github.com/nickng/dingo-hunter"
  function init (line 47) | func init() {
  function Serve (line 64) | func Serve() {

FILE: examples/altbit/main.go
  function main (line 11) | func main() {
  function flip (line 18) | func flip(b int) int {
  function tx (line 22) | func tx(snd chan<- int, ack <-chan int) {
  function rx (line 51) | func rx(reply chan<- int, trans <-chan int) {

FILE: examples/branch-dependent-deadlock/main.go
  function S (line 3) | func S(ch chan int, done chan struct{}) {
  function R (line 8) | func R(ch chan int, done chan struct{}) {
  function main (line 13) | func main() {

FILE: examples/channel-scoping-test/main.go
  function main (line 8) | func main() {
  function makenew (line 17) | func makenew() chan int {

FILE: examples/commaok/main.go
  function main (line 7) | func main() {

FILE: examples/cond-recur/main.go
  function x (line 7) | func x(ch chan int, done chan struct{}) {
  function main (line 21) | func main() {

FILE: examples/deadlocking-philosophers/main.go
  type Philosopher (line 12) | type Philosopher struct
    method think (line 24) | func (phil *Philosopher) think() {
    method eat (line 29) | func (phil *Philosopher) eat() {
    method getChopsticks (line 34) | func (phil *Philosopher) getChopsticks() {
    method returnChopsticks (line 45) | func (phil *Philosopher) returnChopsticks() {
    method dine (line 50) | func (phil *Philosopher) dine(announce chan *Philosopher) {
  function makePhilosopher (line 18) | func makePhilosopher(name string, neighbor *Philosopher) *Philosopher {
  function main (line 58) | func main() {

FILE: examples/dining-philosophers/main.go
  type Philosopher (line 12) | type Philosopher struct
    method think (line 24) | func (phil *Philosopher) think() {
    method eat (line 29) | func (phil *Philosopher) eat() {
    method getChopsticks (line 34) | func (phil *Philosopher) getChopsticks() {
    method returnChopsticks (line 51) | func (phil *Philosopher) returnChopsticks() {
    method dine (line 56) | func (phil *Philosopher) dine(announce chan *Philosopher) {
  function makePhilosopher (line 18) | func makePhilosopher(name string, neighbor *Philosopher) *Philosopher {
  function main (line 64) | func main() {

FILE: examples/factorial/main.go
  function main (line 5) | func main() {
  function fact (line 11) | func fact(n int, results chan<- int) {

FILE: examples/fanin-pattern-commaok/main.go
  function work (line 11) | func work(out chan<- int) {
  function fanin (line 17) | func fanin(ch1, ch2 <-chan int) <-chan int {
  function main (line 35) | func main() {

FILE: examples/fanin-pattern/main.go
  function work (line 7) | func work(out chan<- int) {
  function fanin (line 13) | func fanin(ch1, ch2 <-chan int) <-chan int {
  function main (line 28) | func main() {

FILE: examples/fcall/main.go
  function f (line 7) | func f() {
  function g (line 12) | func g() {
  function main (line 17) | func main() {

FILE: examples/forselect/main.go
  function sel1 (line 7) | func sel1(ch1, ch2 chan int, done chan struct{}) {
  function sel2 (line 20) | func sel2(ch1, ch2 chan int, done chan struct{}) {
  function main (line 33) | func main() {

FILE: examples/giachino-concur14-dining-philosopher/main.go
  function Fork (line 11) | func Fork(fork chan int) {
  function phil (line 19) | func phil(fork1, fork2 chan int, id int) {
  function main (line 45) | func main() {

FILE: examples/giachino-concur14-factorial/main.go
  function fact (line 8) | func fact(n int, r, s chan int) {
  function main (line 20) | func main() {

FILE: examples/github-golang-go-issue-12734/main.go
  function useless (line 11) | func useless(address string) []byte {
  function test_a (line 16) | func test_a(test_channel chan int) {
  function test (line 21) | func test() {
  function main (line 30) | func main() {

FILE: examples/golang-blog-prime-sieve/main.go
  function Generate (line 11) | func Generate(ch chan<- int) {
  function Filter (line 19) | func Filter(in <-chan int, out chan<- int, prime int) {
  function main (line 29) | func main() {

FILE: examples/infinite-prime-sieve/main.go
  function Generate (line 14) | func Generate(ch chan<- int) {
  function Filter (line 22) | func Filter(in <-chan int, out chan<- int, prime int) {
  function main (line 32) | func main() {

FILE: examples/issue-10-close-wrong-migo-chan-name/main.go
  function main (line 6) | func main() {

FILE: examples/issue-11-non-communicating-fn-call/main.go
  function main (line 6) | func main() {

FILE: examples/jobsched/main.go
  function worker (line 10) | func worker(id int, jobQueue <-chan int, done <-chan struct{}) {
  function morejob (line 22) | func morejob() bool {
  function producer (line 27) | func producer(q chan int, done chan struct{}) {
  function main (line 34) | func main() {

FILE: examples/local-deadlock-fixed/main.go
  function Work (line 8) | func Work() {
  function Send (line 15) | func Send(ch chan<- int)                  { ch <- 42 }
  function Recv (line 16) | func Recv(ch <-chan int, done chan<- int) { done <- <-ch }
  function main (line 18) | func main() {

FILE: examples/local-deadlock/main.go
  function Work (line 8) | func Work() {
  function Send (line 15) | func Send(ch chan<- int)                  { ch <- 42 }
  function Recv (line 16) | func Recv(ch <-chan int, done chan<- int) { done <- <-ch }
  function main (line 18) | func main() {

FILE: examples/loop-variations/main.go
  function main (line 8) | func main() {

FILE: examples/makechan-in-loop/main.go
  function main (line 9) | func main() {

FILE: examples/md5/main.go
  type result (line 17) | type result struct
  function sumFiles (line 27) | func sumFiles(done <-chan struct{}, root string) (<-chan result, <-chan ...
  function MD5All (line 74) | func MD5All(root string) (map[string][md5.Size]byte, error) {
  function main (line 95) | func main() {

FILE: examples/multi-makechan-same-var/main.go
  function createChan (line 6) | func createChan() chan int {
  function main (line 10) | func main() {

FILE: examples/multiple-files/main.go
  function main (line 7) | func main() {

FILE: examples/multiple-files/x.go
  function x (line 5) | func x() {

FILE: examples/multiple-timeout/main.go
  function main (line 11) | func main() {

FILE: examples/parallel-buffered-recursive-fibonacci/main.go
  function main (line 7) | func main() {
  function fib (line 13) | func fib(n int, ch chan<- int) {

FILE: examples/parallel-recursive-fibonacci/main.go
  function main (line 7) | func main() {
  function fib (line 13) | func fib(n int, ch chan<- int) {

FILE: examples/parallel-twoprocess-fibonacci/main.go
  function fib (line 7) | func fib(n int) int {
  function fibParallel (line 14) | func fibParallel(n int, ch chan<- int) {
  function main (line 18) | func main() {

FILE: examples/philo/main.go
  function philo (line 9) | func philo(id int, forks chan int) {
  function main (line 19) | func main() {

FILE: examples/popl17/alt-bit/main.go
  function main (line 9) | func main() {
  function tx (line 16) | func tx(snd chan<- int, ack <-chan int) {
  function rx (line 44) | func rx(reply chan<- int, trans <-chan int) {

FILE: examples/popl17/concsys/main.go
  type Result (line 9) | type Result
  type Search (line 10) | type Search
  function fakeSearch (line 12) | func fakeSearch(kind string) Search {
  function SequentialSearch (line 33) | func SequentialSearch(query string) (results []Result) {
  function ConcurrentSearch (line 40) | func ConcurrentSearch(query string) (results []Result) {
  function ConcurrentSearchWithCutOff (line 53) | func ConcurrentSearchWithCutOff(query string) (results []Result) {
  function First (line 72) | func First(query string, replicas ...Search) Result {
  function ReplicaSearch (line 81) | func ReplicaSearch(query string) (results []Result) {
  function main (line 100) | func main() {

FILE: examples/popl17/cond-recur/main.go
  function x (line 7) | func x(ch chan int, done chan struct{}) {
  function main (line 21) | func main() {

FILE: examples/popl17/dinephil/main.go
  function Fork (line 11) | func Fork(fork chan int) {
  function phil (line 19) | func phil(fork1, fork2 chan int, id int) {
  function main (line 45) | func main() {

FILE: examples/popl17/fact/main.go
  function main (line 5) | func main() {
  function fact (line 11) | func fact(n int, results chan<- int) {

FILE: examples/popl17/fanin-alt/main.go
  function work (line 11) | func work(out chan<- int) {
  function fanin (line 17) | func fanin(ch1, ch2, c chan int) {
  function main (line 36) | func main() {

FILE: examples/popl17/fanin/main.go
  function work1 (line 7) | func work1(out chan<- int) {
  function work2 (line 12) | func work2(out chan<- int) {
  function fanin (line 18) | func fanin(ch1, ch2, c chan int) {
  function main (line 34) | func main() {

FILE: examples/popl17/fib-async/main.go
  function main (line 7) | func main() {
  function fib (line 13) | func fib(n int, ch chan<- int) {

FILE: examples/popl17/fib/main.go
  function main (line 7) | func main() {
  function fib (line 13) | func fib(n int, ch chan<- int) {

FILE: examples/popl17/fixed/main.go
  function Work (line 8) | func Work() {
  function Send (line 15) | func Send(ch chan<- int)                  { ch <- 42 }
  function Recv (line 16) | func Recv(ch <-chan int, done chan<- int) { done <- <-ch }
  function main (line 18) | func main() {

FILE: examples/popl17/forselect/main.go
  function sel1 (line 7) | func sel1(ch1, ch2 chan int, done chan struct{}) {
  function sel2 (line 20) | func sel2(ch1, ch2 chan int, done chan struct{}) {
  function main (line 33) | func main() {

FILE: examples/popl17/jobsched/main.go
  function worker (line 10) | func worker(id int, jobQueue <-chan int, done <-chan struct{}) {
  function morejob (line 22) | func morejob() bool {
  function producer (line 27) | func producer(q chan int, done chan struct{}) {
  function main (line 34) | func main() {

FILE: examples/popl17/mismatch/main.go
  function Work (line 8) | func Work() {
  function Send (line 15) | func Send(ch chan<- int)                  { ch <- 42 }
  function Recv (line 16) | func Recv(ch <-chan int, done chan<- int) { done <- <-ch }
  function main (line 18) | func main() {

FILE: examples/popl17/sieve/main.go
  function Generate (line 14) | func Generate(ch chan<- int) {
  function Filter (line 22) | func Filter(in <-chan int, out chan<- int, prime int) {
  function main (line 32) | func main() {

FILE: examples/popl17ae/close/main.go
  function main (line 3) | func main() {

FILE: examples/popl17ae/emptyselect/main.go
  function s (line 3) | func s(ch chan int) {
  function main (line 7) | func main() {

FILE: examples/powsers/powser1.go
  type rat (line 22) | type rat struct
    method pr (line 26) | func (u rat) pr() {
    method eq (line 35) | func (u rat) eq(c rat) bool {
  type dch (line 39) | type dch struct
  type dch2 (line 45) | type dch2
  function mkdch (line 51) | func mkdch() *dch {
  function mkdch2 (line 61) | func mkdch2() *dch2 {
  function dosplit (line 82) | func dosplit(in *dch, out *dch2, wait chan int) {
  function split (line 112) | func split(in *dch, out *dch2) {
  function put (line 118) | func put(dat rat, out *dch) {
  function get (line 123) | func get(in *dch) rat {
  function getn (line 131) | func getn(in []*dch) []rat {
  function get2 (line 168) | func get2(in0 *dch, in1 *dch) []rat {
  function copy (line 172) | func copy(in *dch, out *dch) {
  function repeat (line 179) | func repeat(dat rat, out *dch) {
  type PS (line 185) | type PS
  type PS2 (line 186) | type PS2
  function mkPS (line 191) | func mkPS() *dch {
  function mkPS2 (line 195) | func mkPS2() *dch2 {
  function gcd (line 207) | func gcd(u, v int64) int64 {
  function i2tor (line 219) | func i2tor(u, v int64) rat {
  function itor (line 232) | func itor(u int64) rat {
  function end (line 243) | func end(u rat) int64 {
  function add (line 252) | func add(u, v rat) rat {
  function mul (line 257) | func mul(u, v rat) rat {
  function neg (line 266) | func neg(u rat) rat {
  function sub (line 270) | func sub(u, v rat) rat {
  function inv (line 274) | func inv(u rat) rat { // invert a rat
  function evaln (line 282) | func evaln(c rat, U PS, n int) {
  function printn (line 298) | func printn(U PS, n int) {
  function eval (line 312) | func eval(c rat, U PS, n int) rat {
  function Split (line 329) | func Split(U PS) *dch2 {
  function Add (line 336) | func Add(U, V PS) PS {
  function Cmul (line 361) | func Cmul(c rat, U PS) PS {
  function Sub (line 381) | func Sub(U, V PS) PS {
  function Monmul (line 387) | func Monmul(U PS, n int) PS {
  function Xmul (line 400) | func Xmul(U PS) PS {
  function Rep (line 404) | func Rep(c rat) PS {
  function Mon (line 412) | func Mon(c rat, n int) PS {
  function Shift (line 426) | func Shift(c rat, U PS) PS {
  function Mul (line 461) | func Mul(U, V PS) PS {
  function Diff (line 483) | func Diff(U PS) PS {
  function Integ (line 506) | func Integ(c rat, U PS) PS {
  function Binom (line 526) | func Binom(c rat) PS {
  function Recip (line 550) | func Recip(U PS) PS {
  function Exp (line 570) | func Exp(U PS) PS {
  function Subst (line 582) | func Subst(U, V PS) PS {
  function MonSubst (line 603) | func MonSubst(U PS, c0 rat, n int) PS {
  function Init (line 625) | func Init() {
  function check (line 636) | func check(U PS, c rat, count int, str string) {
  constant N (line 650) | N = 10
  function checka (line 652) | func checka(U PS, a []rat, str string) {
  function main (line 658) | func main() {

FILE: examples/producer-consumer/main.go
  function produce (line 11) | func produce() {
  function consume (line 18) | func consume() {
  function main (line 25) | func main() {

FILE: examples/ring-pattern/main.go
  function numprocs (line 5) | func numprocs() int {
  function adder (line 9) | func adder(in <-chan int, out chan<- int) {
  function main (line 15) | func main() {

FILE: examples/russ-cox-fizzbuzz/main.go
  function main (line 5) | func main() {
  function generate (line 18) | func generate() <-chan string {
  function filter (line 28) | func filter(c <-chan string, n int, label string) <-chan string {

FILE: examples/select-with-continuation/main.go
  function main (line 7) | func main() {

FILE: examples/select-with-weak-mismatch/main.go
  function main (line 9) | func main() {

FILE: examples/semaphores/main.go
  type empty (line 7) | type empty struct
  type Semaphore (line 8) | type Semaphore
    method P (line 15) | func (s Semaphore) P(n int) {
    method V (line 24) | func (s Semaphore) V(n int) {
  function NewSemaphore (line 10) | func NewSemaphore(cap int) Semaphore {
  function main (line 31) | func main() {

FILE: examples/send-recv-with-interfaces/main.go
  type Interacter (line 7) | type Interacter interface
  type S (line 12) | type S struct
    method Send (line 14) | func (st S) Send(ch chan int) {
    method Recv (line 18) | func (st S) Recv(ch chan int) {
  function main (line 22) | func main() {

FILE: examples/simple/main.go
  function main (line 7) | func main() {

FILE: examples/single-gortn-method-call/main.go
  type T (line 5) | type T struct
    method setup (line 10) | func (t *T) setup(y int) {
  function main (line 15) | func main() {

FILE: examples/spawn-in-choice/main.go
  function S (line 11) | func S(out chan int) {
  function R (line 16) | func R(in chan int) {
  function main (line 21) | func main() {

FILE: examples/squaring-cancellation/main.go
  function gen (line 12) | func gen(done <-chan struct{}, nums ...int) <-chan int {
  function sq (line 27) | func sq(done <-chan struct{}, in <-chan int) <-chan int {
  function merge (line 42) | func merge(done <-chan struct{}, cs ...<-chan int) <-chan int {
  function main (line 72) | func main() {

FILE: examples/squaring-fanin-bad/main.go
  function gen (line 17) | func gen(nums ...int) <-chan int {
  function sq (line 32) | func sq(in <-chan int) <-chan int {
  function merge (line 43) | func merge(cs ...<-chan int) <-chan int {
  function main (line 69) | func main() {

FILE: examples/squaring-fanin/main.go
  function gen (line 16) | func gen(nums ...int) <-chan int {
  function sq (line 31) | func sq(in <-chan int) <-chan int {
  function merge (line 42) | func merge(cs ...<-chan int) <-chan int {
  function main (line 68) | func main() {

FILE: examples/squaring-pipeline/main.go
  function gen (line 13) | func gen(nums ...int) <-chan int {
  function sq (line 28) | func sq(in <-chan int) <-chan int {
  function main (line 42) | func main() {

FILE: examples/struct-done-channel/main.go
  type T (line 3) | type T struct
  function X (line 8) | func X(ctx T) {
  function main (line 12) | func main() {

FILE: examples/timeout-behaviour/main.go
  function main (line 8) | func main() {

FILE: fairness/fairness.go
  type FairnessAnalysis (line 23) | type FairnessAnalysis struct
    method Visit (line 35) | func (fa *FairnessAnalysis) Visit(fn *ssa.Function) {
    method isLikelyUnsafe (line 96) | func (fa *FairnessAnalysis) isLikelyUnsafe(blk *ssa.BasicBlock) bool {
    method isCondFair (line 120) | func (fa *FairnessAnalysis) isCondFair(cond ssa.Value) bool {
    method isIndexStatic (line 149) | func (fa *FairnessAnalysis) isIndexStatic(blk *ssa.BasicBlock) bool {
  function NewFairnessAnalysis (line 31) | func NewFairnessAnalysis() *FairnessAnalysis {
  function Check (line 165) | func Check(info *ssabuilder.SSAInfo) {

FILE: logwriter/logwriter.go
  type Writer (line 17) | type Writer struct
    method Create (line 45) | func (w *Writer) Create() error {
  function NewFile (line 27) | func NewFile(logfile string, enableLogging, enableColour bool) *Writer {
  function New (line 36) | func New(w io.Writer, enableLogging, enableColour bool) *Writer {

FILE: main.go
  function main (line 12) | func main() {

FILE: migoextract/call.go
  method Call (line 14) | func (caller *Function) Call(call *ssa.Call, infer *TypeInfer, b *Block,...
  method Go (line 92) | func (caller *Function) Go(instr *ssa.Go, infer *TypeInfer) {
  method callLen (line 119) | func (caller *Function) callLen(common *ssa.CallCommon, infer *TypeInfer...
  method storeRetvals (line 137) | func (caller *Function) storeRetvals(infer *TypeInfer, retval ssa.Value,...
  method IsRecursiveCall (line 241) | func (caller *Function) IsRecursiveCall() bool {
  method invoke (line 250) | func (caller *Function) invoke(common *ssa.CallCommon, infer *TypeInfer,...
  method callFn (line 295) | func (caller *Function) callFn(common *ssa.CallCommon, infer *TypeInfer,...
  method call (line 299) | func (caller *Function) call(common *ssa.CallCommon, fn *ssa.Function, r...
  method callClosure (line 329) | func (caller *Function) callClosure(common *ssa.CallCommon, closure *ssa...
  function findMethod (line 339) | func findMethod(prog *ssa.Program, meth *types.Func, typ types.Type, inf...

FILE: migoextract/chan.go
  function getChan (line 11) | func getChan(val ssa.Value, infer *TypeInfer) ssa.Value {

FILE: migoextract/closure.go
  type Captures (line 4) | type Captures

FILE: migoextract/commaok.go
  type CommaOk (line 11) | type CommaOk struct
  function isCommaOk (line 17) | func isCommaOk(f *Function, inst Instance) bool {

FILE: migoextract/context.go
  type LoopState (line 17) | type LoopState
  constant NonLoop (line 21) | NonLoop LoopState = iota
  constant Enter (line 22) | Enter
  constant Body (line 23) | Body
  constant Exit (line 24) | Exit
  type LoopBound (line 28) | type LoopBound
  constant Unknown (line 32) | Unknown LoopBound = iota
  constant Static (line 33) | Static
  constant Dynamic (line 34) | Dynamic
  type Program (line 41) | type Program struct
  function NewProgram (line 52) | func NewProgram(infer *TypeInfer) *Program {
  type Function (line 67) | type Function struct
    method HasBody (line 138) | func (caller *Function) HasBody() bool { return caller.hasBody }
    method prepareCallFn (line 142) | func (caller *Function) prepareCallFn(common *ssa.CallCommon, fn *ssa....
    method InstanceID (line 201) | func (caller *Function) InstanceID() int {
    method String (line 208) | func (caller *Function) String() string {
    method updateInstances (line 227) | func (caller *Function) updateInstances(old, new Instance) {
  function NewMainFunction (line 91) | func NewMainFunction(prog *Program, mainFn *ssa.Function) *Function {
  function NewFunction (line 114) | func NewFunction(caller *Function) *Function {
  type Block (line 266) | type Block struct
  function NewBlock (line 274) | func NewBlock(parent *Function, block *ssa.BasicBlock, curr int) *Block {
  type Context (line 286) | type Context struct

FILE: migoextract/datastructure.go
  type Elems (line 12) | type Elems
  type Fields (line 15) | type Fields
  method getStructField (line 17) | func (caller *Function) getStructField(struc ssa.Value, idx int) (Instan...
  method setStructField (line 28) | func (caller *Function) setStructField(struc ssa.Value, idx int, instanc...
  function initNestedRefVar (line 42) | func initNestedRefVar(infer *TypeInfer, ctx *Context, inst Instance, hea...

FILE: migoextract/instance.go
  type Instance (line 16) | type Instance interface
  type Value (line 23) | type Value struct
    method Instance (line 30) | func (i *Value) Instance() (int, int) {
    method String (line 34) | func (i *Value) String() string {
  type Placeholder (line 45) | type Placeholder struct
    method Instance (line 49) | func (i *Placeholder) Instance() (int, int) {
    method String (line 53) | func (i *Placeholder) String() string {
  type External (line 61) | type External struct
    method Instance (line 68) | func (i *External) Instance() (int, int) {
    method String (line 72) | func (i *External) String() string {
  type Const (line 85) | type Const struct
    method Instance (line 90) | func (c *Const) Instance() (int, int) { return 0, 0 }
    method String (line 92) | func (c *Const) String() string {

FILE: migoextract/loop.go
  type Loop (line 17) | type Loop struct
    method SetInit (line 32) | func (l *Loop) SetInit(index ssa.Value, init int64) {
    method SetStep (line 39) | func (l *Loop) SetStep(step int64) {
    method SetCond (line 44) | func (l *Loop) SetCond(cond ssa.Value, max int64) {
    method Next (line 50) | func (l *Loop) Next() {
    method HasNext (line 57) | func (l *Loop) HasNext() bool {
    method String (line 64) | func (l *Loop) String() string {
  function loopSetIndex (line 72) | func loopSetIndex(instr *ssa.Phi, infer *TypeInfer, ctx *Context) {
  function loopDetectBounds (line 97) | func loopDetectBounds(instr *ssa.Phi, infer *TypeInfer, ctx *Context) {
  function loopStateTransition (line 128) | func loopStateTransition(blk *ssa.BasicBlock, infer *TypeInfer, f *Funct...

FILE: migoextract/loopbound_string.go
  constant _LoopBound_name (line 7) | _LoopBound_name = "UnknownStaticDynamic"
  method String (line 11) | func (i LoopBound) String() string {

FILE: migoextract/loopstack.go
  type LoopStack (line 8) | type LoopStack struct
    method Push (line 19) | func (s *LoopStack) Push(l *Loop) {
    method Pop (line 26) | func (s *LoopStack) Pop() (*Loop, error) {
    method IsEmpty (line 40) | func (s *LoopStack) IsEmpty() bool {
  function NewLoopStack (line 14) | func NewLoopStack() *LoopStack {

FILE: migoextract/loopstate_string.go
  constant _LoopState_name (line 7) | _LoopState_name = "NonLoopEnterBodyExit"
  method String (line 11) | func (i LoopState) String() string {

FILE: migoextract/migoextract.go
  type TypeInfer (line 17) | type TypeInfer struct
    method Run (line 42) | func (infer *TypeInfer) Run() {
    method RunQueue (line 85) | func (infer *TypeInfer) RunQueue() {
  function New (line 29) | func New(ssainfo *ssabuilder.SSAInfo, inferlog io.Writer) (*TypeInfer, e...

FILE: migoextract/phi.go
  function phiSelectEdge (line 10) | func phiSelectEdge(instr *ssa.Phi, infer *TypeInfer, ctx *Context) (edge...

FILE: migoextract/pointer.go
  function derefType (line 10) | func derefType(t types.Type) types.Type {
  function derefAllType (line 18) | func derefAllType(t types.Type) types.Type {

FILE: migoextract/print.go
  constant BlockSymbol (line 13) | BlockSymbol     = "┝ "
  constant CallSymbol (line 14) | CallSymbol      = " ↘ "
  constant ExitSymbol (line 15) | ExitSymbol      = " ↙ "
  constant ChanSymbol (line 16) | ChanSymbol      = "ν "
  constant FuncEnterSymbol (line 17) | FuncEnterSymbol = "┌"
  constant FuncExitSymbol (line 18) | FuncExitSymbol  = "└"
  constant RecvSymbol (line 19) | RecvSymbol      = "❓ "
  constant SendSymbol (line 20) | SendSymbol      = "❗ "
  constant SkipSymbol (line 21) | SkipSymbol      = "┆ "
  constant SpawnSymbol (line 22) | SpawnSymbol     = "┿ "
  constant JumpSymbol (line 23) | JumpSymbol      = " ⇾ "
  constant ParamSymbol (line 24) | ParamSymbol     = "├param "
  constant MoreSymbol (line 25) | MoreSymbol      = "┊ "
  constant ReturnSymbol (line 26) | ReturnSymbol    = "⏎ "
  constant LoopSymbol (line 27) | LoopSymbol      = "↻ "
  constant PhiSymbol (line 28) | PhiSymbol       = "φ "
  constant IfSymbol (line 29) | IfSymbol        = "⨁ "
  constant SelectSymbol (line 30) | SelectSymbol    = "Sel:"
  constant SplitSymbol (line 31) | SplitSymbol     = "分"
  constant ErrorSymbol (line 32) | ErrorSymbol     = " ◹ "
  constant FieldSymbol (line 33) | FieldSymbol     = " ↦ "
  constant NewSymbol (line 34) | NewSymbol       = "新"
  constant SubSymbol (line 35) | SubSymbol       = "    ▸ "
  constant ValSymbol (line 36) | ValSymbol       = "├ "
  constant AssignSymbol (line 37) | AssignSymbol    = "≔"
  method Sprintf (line 53) | func (ctx *Function) Sprintf(format string, a ...interface{}) string {

FILE: migoextract/select.go
  type Select (line 9) | type Select struct

FILE: migoextract/storage.go
  type Storage (line 4) | type Storage struct
  function NewStorage (line 11) | func NewStorage() *Storage {

FILE: migoextract/tuple.go
  type Tuples (line 7) | type Tuples

FILE: migoextract/visit.go
  function visitFunc (line 14) | func visitFunc(fn *ssa.Function, infer *TypeInfer, f *Function) {
  function visitBasicBlock (line 42) | func visitBasicBlock(blk *ssa.BasicBlock, infer *TypeInfer, f *Function,...
  function visitInstr (line 80) | func visitInstr(instr ssa.Instruction, infer *TypeInfer, ctx *Context) {
  function visitAlloc (line 158) | func visitAlloc(instr *ssa.Alloc, infer *TypeInfer, ctx *Context) {
  function visitBinOp (line 210) | func visitBinOp(instr *ssa.BinOp, infer *TypeInfer, ctx *Context) {
  function visitCall (line 266) | func visitCall(instr *ssa.Call, infer *TypeInfer, ctx *Context) {
  function visitChangeType (line 271) | func visitChangeType(instr *ssa.ChangeType, infer *TypeInfer, ctx *Conte...
  function visitChangeInterface (line 295) | func visitChangeInterface(instr *ssa.ChangeInterface, infer *TypeInfer, ...
  function visitConvert (line 303) | func visitConvert(instr *ssa.Convert, infer *TypeInfer, ctx *Context) {
  function visitDefer (line 325) | func visitDefer(instr *ssa.Defer, infer *TypeInfer, ctx *Context) {
  function visitDeref (line 329) | func visitDeref(instr *ssa.UnOp, infer *TypeInfer, ctx *Context) {
  function visitExtract (line 362) | func visitExtract(instr *ssa.Extract, infer *TypeInfer, ctx *Context) {
  function visitField (line 402) | func visitField(instr *ssa.Field, infer *TypeInfer, ctx *Context) {
  function visitFieldAddr (line 432) | func visitFieldAddr(instr *ssa.FieldAddr, infer *TypeInfer, ctx *Context) {
  function visitGo (line 483) | func visitGo(instr *ssa.Go, infer *TypeInfer, ctx *Context) {
  function visitIf (line 488) | func visitIf(instr *ssa.If, infer *TypeInfer, ctx *Context) {
  function visitIndex (line 641) | func visitIndex(instr *ssa.Index, infer *TypeInfer, ctx *Context) {
  function visitIndexAddr (line 674) | func visitIndexAddr(instr *ssa.IndexAddr, infer *TypeInfer, ctx *Context) {
  function visitJump (line 776) | func visitJump(jump *ssa.Jump, infer *TypeInfer, ctx *Context) {
  function visitLookup (line 847) | func visitLookup(instr *ssa.Lookup, infer *TypeInfer, ctx *Context) {
  function visitMakeChan (line 877) | func visitMakeChan(instr *ssa.MakeChan, infer *TypeInfer, ctx *Context) {
  function visitMakeClosure (line 911) | func visitMakeClosure(instr *ssa.MakeClosure, infer *TypeInfer, ctx *Con...
  function visitMakeInterface (line 920) | func visitMakeInterface(instr *ssa.MakeInterface, infer *TypeInfer, ctx ...
  function visitMakeMap (line 934) | func visitMakeMap(instr *ssa.MakeMap, infer *TypeInfer, ctx *Context) {
  function visitMakeSlice (line 940) | func visitMakeSlice(instr *ssa.MakeSlice, infer *TypeInfer, ctx *Context) {
  function visitMapUpdate (line 946) | func visitMapUpdate(instr *ssa.MapUpdate, infer *TypeInfer, ctx *Context) {
  function visitNext (line 976) | func visitNext(instr *ssa.Next, infer *TypeInfer, ctx *Context) {
  function visitPhi (line 982) | func visitPhi(instr *ssa.Phi, infer *TypeInfer, ctx *Context) {
  function visitRecv (line 999) | func visitRecv(instr *ssa.UnOp, infer *TypeInfer, ctx *Context) {
  function visitReturn (line 1028) | func visitReturn(ret *ssa.Return, infer *TypeInfer, ctx *Context) {
  function visitRunDefers (line 1051) | func visitRunDefers(instr *ssa.RunDefers, infer *TypeInfer, ctx *Context) {
  function visitSelect (line 1070) | func visitSelect(instr *ssa.Select, infer *TypeInfer, ctx *Context) {
  function visitSend (line 1123) | func visitSend(instr *ssa.Send, infer *TypeInfer, ctx *Context) {
  function visitSkip (line 1141) | func visitSkip(instr ssa.Instruction, infer *TypeInfer, ctx *Context) {
  function visitSlice (line 1149) | func visitSlice(instr *ssa.Slice, infer *TypeInfer, ctx *Context) {
  function visitStore (line 1188) | func visitStore(instr *ssa.Store, infer *TypeInfer, ctx *Context) {
  function visitTypeAssert (line 1257) | func visitTypeAssert(instr *ssa.TypeAssert, infer *TypeInfer, ctx *Conte...

FILE: ssabuilder/callgraph/callgraph.go
  type Node (line 9) | type Node struct
    method Traverse (line 37) | func (node *Node) Traverse(v FuncVisitor) {
  function Build (line 19) | func Build(main *ssa.Function) *Node {
  type FuncVisitor (line 32) | type FuncVisitor interface
  function visitBlock (line 44) | func visitBlock(b *ssa.BasicBlock, node *Node) {

FILE: ssabuilder/channel.go
  type ChanOpType (line 14) | type ChanOpType
  constant ChanMake (line 17) | ChanMake ChanOpType = iota
  constant ChanSend (line 18) | ChanSend
  constant ChanRecv (line 19) | ChanRecv
  constant ChanClose (line 20) | ChanClose
  type ChanOp (line 24) | type ChanOp struct
  function chanOps (line 31) | func chanOps(instr ssa.Instruction) []ChanOp {
  function progChanOps (line 59) | func progChanOps(prog *ssa.Program) []ChanOp {
  function purgeChanOps (line 78) | func purgeChanOps(ops []ChanOp, ch ssa.Value) []ChanOp {

FILE: ssabuilder/pointer.go
  function setupPTA (line 17) | func setupPTA(prog *ssa.Program, lprog *loader.Program, ptaLog io.Writer...

FILE: ssabuilder/print.go
  type Members (line 11) | type Members
    method Len (line 13) | func (m Members) Len() int           { return len(m) }
    method Less (line 14) | func (m Members) Less(i, j int) bool { return m[i].Pos() < m[j].Pos() }
    method Swap (line 15) | func (m Members) Swap(i, j int)      { m[i], m[j] = m[j], m[i] }
  method WriteTo (line 18) | func (info *SSAInfo) WriteTo(w io.Writer) (int64, error) {
  method WriteAll (line 42) | func (info *SSAInfo) WriteAll(w io.Writer) (int64, error) {

FILE: ssabuilder/ssabuild.go
  type Mode (line 22) | type Mode
  constant FromFiles (line 26) | FromFiles Mode = 1 << iota
  constant FromString (line 29) | FromString
  type Config (line 33) | type Config struct
    method Build (line 96) | func (conf *Config) Build() (*SSAInfo, error) {
  type SSAInfo (line 44) | type SSAInfo struct
    method CallGraph (line 162) | func (info *SSAInfo) CallGraph() *callgraph.Node {
    method DecodePos (line 173) | func (info *SSAInfo) DecodePos(pos token.Pos) token.Position {
    method NewPta (line 178) | func (info *SSAInfo) NewPta(vals ...ssa.Value) *pointer.Result {
    method FindChan (line 191) | func (info *SSAInfo) FindChan(ch ssa.Value) []ChanOp {
  function NewConfig (line 69) | func NewConfig(files []string) (*Config, error) {
  function NewConfigFromString (line 84) | func NewConfigFromString(s string) (*Config, error) {

FILE: ssabuilder/util.go
  function MainPkg (line 8) | func MainPkg(prog *ssa.Program) *ssa.Package {

FILE: static/script.js
  function goCode (line 2) | function goCode() {
  function migoCode (line 9) | function migoCode() {
  function writeTo (line 17) | function writeTo(s, selector) {
  function reportTime (line 25) | function reportTime(t) {

FILE: talks/basic/concurrency.go
  function deepThought (line 10) | func deepThought(replyCh chan int) {
  function main (line 15) | func main() {

FILE: talks/basic/fanin.go
  function work (line 9) | func work(out chan<- int) {
  function fanin (line 14) | func fanin(input1, input2 <-chan int) <-chan int {
  function main (line 29) | func main() {

FILE: talks/basic/select.go
  function calc (line 11) | func calc(ch chan int) {
  function main (line 18) | func main() {

FILE: talks/deadlock/deadlock.go
  function Send (line 14) | func Send(ch chan<- int)                     { ch <- 42 }
  function RecvAck (line 15) | func RecvAck(ch <-chan int, done chan<- int) { v := <-ch; done <- v }
  function main (line 17) | func main() {

FILE: talks/static/dir.js
  function bindEvent (line 7) | function bindEvent(el, e, fn) {
  function godocs_bindSearchEvents (line 15) | function godocs_bindSearchEvents() {

FILE: talks/static/jquery-ui.js
  function i (line 6) | function i(t,i){var a,n,r,o=t.nodeName.toLowerCase();return"area"===o?(a...
  function s (line 6) | function s(t){return e.expr.filters.visible(t)&&!e(t).parents().addBack(...
  function a (line 6) | function a(t,i,s,a){return e.each(n,function(){i-=parseFloat(e.css(t,"pa...
  function h (line 6) | function h(){return i||r.options.disabled!==!0&&!e(this).hasClass("ui-st...
  function i (line 6) | function i(){return("string"==typeof e?s[e]:e).apply(s,arguments)}
  function t (line 6) | function t(e){return parseInt(e,10)||0}
  function i (line 6) | function i(e){return!isNaN(parseInt(e,10))}

FILE: talks/static/slides.js
  function hideHelpText (line 137) | function hideHelpText() {
  function getSlideEl (line 141) | function getSlideEl(no) {
  function updateSlideClass (line 149) | function updateSlideClass(slideNo, className) {
  function updateSlides (line 167) | function updateSlides() {
  function prevSlide (line 207) | function prevSlide() {
  function nextSlide (line 216) | function nextSlide() {
  function triggerEnterEvent (line 227) | function triggerEnterEvent(no) {
  function triggerLeaveEvent (line 245) | function triggerLeaveEvent(no) {
  function handleTouchStart (line 265) | function handleTouchStart(event) {
  function handleTouchMove (line 278) | function handleTouchMove(event) {
  function handleTouchEnd (line 288) | function handleTouchEnd(event) {
  function cancelTouch (line 303) | function cancelTouch() {
  function disableSlideFrames (line 310) | function disableSlideFrames(no) {
  function enableSlideFrames (line 322) | function enableSlideFrames(no) {
  function disableFrame (line 334) | function disableFrame(frame) {
  function enableFrame (line 338) | function enableFrame(frame) {
  function setupFrames (line 346) | function setupFrames() {
  function setupInteraction (line 358) | function setupInteraction() {
  function getCurSlideFromHash (line 380) | function getCurSlideFromHash() {
  function updateHash (line 390) | function updateHash() {
  function handleBodyKeyDown (line 396) | function handleBodyKeyDown(event) {
  function addEventListeners (line 437) | function addEventListeners() {
  function addFontStyle (line 443) | function addFontStyle() {
  function addGeneralStyle (line 453) | function addGeneralStyle() {
  function showHelpText (line 471) | function showHelpText() {
  function handleDomLoaded (line 474) | function handleDomLoaded() {
  function initialize (line 494) | function initialize() {

FILE: talks/talk-golanguk-2016/datatype.go
  function main (line 9) | func main() {

FILE: talks/talk-golanguk-2016/deadlock-notdetected.go
  function Sender (line 10) | func Sender(ch chan<- int) {
  function Receiver (line 14) | func Receiver(ch <-chan int, done chan<- int) {
  function main (line 18) | func main() {

FILE: talks/talk-golanguk-2016/deadlock.go
  function Work (line 11) | func Work() {
  function Sender (line 19) | func Sender(ch chan<- int) {
  function Receiver (line 22) | func Receiver(ch <-chan int, done chan<- int) {
  function main (line 26) | func main() {

FILE: webservice/cfsm.go
  function cfsmHandler (line 15) | func cfsmHandler(w http.ResponseWriter, req *http.Request) {

FILE: webservice/errors.go
  type ErrInternal (line 9) | type ErrInternal struct
    method Error (line 18) | func (e *ErrInternal) Error() string {
    method Report (line 23) | func (e *ErrInternal) Report(w http.ResponseWriter) {
  function NewErrInternal (line 14) | func NewErrInternal(cause error, message string) *ErrInternal {

FILE: webservice/gong.go
  function gongHandler (line 14) | func gongHandler(w http.ResponseWriter, req *http.Request) {

FILE: webservice/handlers.go
  function indexHandler (line 19) | func indexHandler(w http.ResponseWriter, req *http.Request) {
  function loadHandler (line 48) | func loadHandler(w http.ResponseWriter, req *http.Request) {

FILE: webservice/migo.go
  function migoHandler (line 14) | func migoHandler(w http.ResponseWriter, req *http.Request) {

FILE: webservice/play.go
  constant basePkg (line 18) | basePkg = "golang.org/x/tools/cmd/present"
  function initPlayground (line 22) | func initPlayground(origin *url.URL) {
  function playScript (line 33) | func playScript(root, transport string) {

FILE: webservice/server.go
  type Server (line 12) | type Server struct
    method Start (line 27) | func (s *Server) Start() {
    method Close (line 44) | func (s *Server) Close() {
    method URL (line 48) | func (s *Server) URL() string {
    method Listener (line 52) | func (s *Server) Listener() net.Listener {
  function NewServer (line 20) | func NewServer(iface string, port string) *Server {

FILE: webservice/ssa.go
  function ssaHandler (line 10) | func ssaHandler(w http.ResponseWriter, req *http.Request) {

FILE: webservice/synthesis.go
  function synthesisHandler (line 16) | func synthesisHandler(w http.ResponseWriter, req *http.Request) {
Condensed preview — 171 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (450K chars).
[
  {
    "path": ".gitignore",
    "chars": 305,
    "preview": "# 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 spe"
  },
  {
    "path": ".gitmodules",
    "chars": 140,
    "preview": "[submodule \"third_party/gmc-synthesis\"]\n\tbranch = dingo\n\tpath = third_party/gmc-synthesis\n\turl = https://bitbucket.org/n"
  },
  {
    "path": ".travis.yml",
    "chars": 824,
    "preview": "language: go\nscript:\n    - go test -v ./...\naddons:\n    ssh_known_hosts:\n        - bitbucket.org\nnotifications:\n    slac"
  },
  {
    "path": "LICENSE",
    "chars": 10847,
    "preview": "                              Apache License\n                        Version 2.0, January 2004\n                     http"
  },
  {
    "path": "README.md",
    "chars": 4095,
    "preview": "# dingo-hunter [![Build Status](https://travis-ci.org/nickng/dingo-hunter.svg?branch=master)](https://travis-ci.org/nick"
  },
  {
    "path": "cfsmextract/cfsmextract.go",
    "chars": 3716,
    "preview": "package cfsmextract\n\n// This file contains only the functions needed to start the analysis\n//  - Handle command line fla"
  },
  {
    "path": "cfsmextract/func.go",
    "chars": 16291,
    "preview": "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."
  },
  {
    "path": "cfsmextract/gortn.go",
    "chars": 494,
    "preview": "package cfsmextract\n\nimport (\n\t\"github.com/nickng/dingo-hunter/cfsmextract/sesstype\"\n\t\"golang.org/x/tools/go/ssa\"\n)\n\ntyp"
  },
  {
    "path": "cfsmextract/sesstype/cfsm.go",
    "chars": 4733,
    "preview": "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 "
  },
  {
    "path": "cfsmextract/sesstype/dot.go",
    "chars": 4121,
    "preview": "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"
  },
  {
    "path": "cfsmextract/sesstype/nodes.go",
    "chars": 557,
    "preview": "package sesstype\n\nimport (\n\t\"fmt\"\n)\n\nfunc CountNodes(root Node) int {\n\ttotal := 1\n\tfor _, child := range root.Children()"
  },
  {
    "path": "cfsmextract/sesstype/nodestack.go",
    "chars": 1059,
    "preview": "package sesstype // import \"github.com/nickng/dingo-hunter/cfsmextract/sesstype\"\n\n// NodeStack is a stack for sesstype.N"
  },
  {
    "path": "cfsmextract/sesstype/nodestack_test.go",
    "chars": 398,
    "preview": "package sesstype\n\nimport (\n\t\"testing\"\n)\n\nfunc TestNewStack(t *testing.T) {\n\tns := NewNodeStack()\n\tns.Push(NewLabelNode(\""
  },
  {
    "path": "cfsmextract/sesstype/op_string.go",
    "chars": 337,
    "preview": "// generated by stringer -type=op; DO NOT EDIT\n\npackage sesstype\n\nimport \"fmt\"\n\nconst _op_name = \"NoOpNewChanOpSendOpRec"
  },
  {
    "path": "cfsmextract/sesstype/sesstype.go",
    "chars": 9732,
    "preview": "// Package sesstype encapsulates representation of session types\n// As opposed to role-based session types, this represe"
  },
  {
    "path": "cfsmextract/sesstype/sesstype_test.go",
    "chars": 5644,
    "preview": "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-hunte"
  },
  {
    "path": "cfsmextract/utils/defs.go",
    "chars": 798,
    "preview": "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 "
  },
  {
    "path": "cfsmextract/utils/emptyvalue.go",
    "chars": 699,
    "preview": "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)"
  },
  {
    "path": "cfsmextract/utils.go",
    "chars": 1166,
    "preview": "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\"gola"
  },
  {
    "path": "cfsmextract/visit.go",
    "chars": 33302,
    "preview": "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/sesstyp"
  },
  {
    "path": "cmd/buildssa.go",
    "chars": 2241,
    "preview": "// Copyright © 2016 Nicholas Ng <nickng@projectfate.org>\n//\n// Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "cmd/cfsms.go",
    "chars": 2393,
    "preview": "// Copyright © 2016 Nicholas Ng <nickng@projectfate.org>\n//\n// Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "cmd/checkfair.go",
    "chars": 1918,
    "preview": "// Copyright © 2016 Nicholas Ng <nickng@projectfate.org>\n//\n// Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "cmd/migo.go",
    "chars": 2575,
    "preview": "// Copyright © 2016 Nicholas Ng <nickng@projectfate.org>\n//\n// Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "cmd/root.go",
    "chars": 2503,
    "preview": "// Copyright © 2016 Nicholas Ng <nickng@projectfate.org>\n//\n// Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "cmd/serve.go",
    "chars": 2198,
    "preview": "// Copyright © 2016 Nicholas Ng <nickng@projectfate.org>\n//\n// Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "doc.go",
    "chars": 298,
    "preview": "// Command dingo-hunter is a tool for analysing Go code to extract the\n// communication patterns for deadlock analysis.\n"
  },
  {
    "path": "examples/altbit/main.go",
    "chars": 1422,
    "preview": "package main\n\n// Alternating bit - from Milner's Communication and Concurrency\n// with (meaningless) timing\n\nimport (\n\t\""
  },
  {
    "path": "examples/branch-dependent-deadlock/main.go",
    "chars": 332,
    "preview": "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 st"
  },
  {
    "path": "examples/channel-scoping-test/main.go",
    "chars": 454,
    "preview": "// The reentry example targets multiple use of a function with channel created\n// inside. The channel is anchored in the"
  },
  {
    "path": "examples/commaok/main.go",
    "chars": 192,
    "preview": "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 := <-"
  },
  {
    "path": "examples/cond-recur/main.go",
    "chars": 516,
    "preview": "// Command conditional-recur has a recursion with conditional on one goroutine\n// and another receiving until a done mes"
  },
  {
    "path": "examples/deadlocking-philosophers/main.go",
    "chars": 2028,
    "preview": "package main\n\n// Dining Philosopher modified to include a deadlock.\n// https://github.com/doug/go-dining-philosophers\n\ni"
  },
  {
    "path": "examples/dining-philosophers/main.go",
    "chars": 2061,
    "preview": "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\"t"
  },
  {
    "path": "examples/factorial/main.go",
    "chars": 244,
    "preview": "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, r"
  },
  {
    "path": "examples/fanin-pattern/main.go",
    "chars": 433,
    "preview": "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) <-"
  },
  {
    "path": "examples/fanin-pattern-commaok/main.go",
    "chars": 659,
    "preview": "package main\n\n// fanin pattern, using for-range loop to consume values (syntactic sugar of\n// loop over r, ok := <-ch)\n\n"
  },
  {
    "path": "examples/fcall/main.go",
    "chars": 296,
    "preview": "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"
  },
  {
    "path": "examples/forselect/main.go",
    "chars": 670,
    "preview": "// Command nodet-for-select is a for-select pattern between two compatible\n// recursive select.\npackage main\n\nimport \"fm"
  },
  {
    "path": "examples/giachino-concur14-dining-philosopher/main.go",
    "chars": 931,
    "preview": "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\""
  },
  {
    "path": "examples/giachino-concur14-factorial/main.go",
    "chars": 409,
    "preview": "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"
  },
  {
    "path": "examples/github-golang-go-issue-12734/main.go",
    "chars": 523,
    "preview": "package main\n\n// This is a bug report from golang/go which the deadlock detector does not\n// detect a deadlock because t"
  },
  {
    "path": "examples/golang-blog-prime-sieve/main.go",
    "chars": 848,
    "preview": "// Command golang-blog-prime-sieve is an example from Golang blog.\n// https://golang.org/doc/play/sieve.go\n//\npackage ma"
  },
  {
    "path": "examples/infinite-prime-sieve/main.go",
    "chars": 962,
    "preview": "// Command infinite-primesieve is a modified primesieve example from Golang\n// blog. The program generates an infinite l"
  },
  {
    "path": "examples/issue-10-close-wrong-migo-chan-name/main.go",
    "chars": 168,
    "preview": "package main\n\n// Issue #10, when generating migo from programs that uses close, the channel\n// name used is incorrect.\n\n"
  },
  {
    "path": "examples/issue-11-non-communicating-fn-call/main.go",
    "chars": 201,
    "preview": "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"
  },
  {
    "path": "examples/jobsched/main.go",
    "chars": 595,
    "preview": "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\tfo"
  },
  {
    "path": "examples/local-deadlock/main.go",
    "chars": 377,
    "preview": "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"
  },
  {
    "path": "examples/local-deadlock-fixed/main.go",
    "chars": 350,
    "preview": "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"
  },
  {
    "path": "examples/loop-variations/main.go",
    "chars": 972,
    "preview": "package main\n\n// This example test different loop and is used for checking loop SSA\n// generation.\n\nimport \"fmt\"\n\nfunc m"
  },
  {
    "path": "examples/makechan-in-loop/main.go",
    "chars": 544,
    "preview": "// The loop-chan example shows what will happen when a channel is created in a\n// loop (and stored in slices). Pointer a"
  },
  {
    "path": "examples/md5/main.go",
    "chars": 2838,
    "preview": "// +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"
  },
  {
    "path": "examples/multi-makechan-same-var/main.go",
    "chars": 248,
    "preview": "// createchan is an example which reuses a channel for different operations in\n// an expanded form.\n\npackage main\n\nfunc "
  },
  {
    "path": "examples/multiple-files/main.go",
    "chars": 109,
    "preview": "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",
    "chars": 65,
    "preview": "package main\n\nimport \"fmt\"\n\nfunc x() {\n\tfmt.Println(\"Hello X\")\n}\n"
  },
  {
    "path": "examples/multiple-timeout/main.go",
    "chars": 522,
    "preview": "// Command multiple-timeout is an example which uses multiple branches of\n// time.After.\n//\n// The main purpose is to te"
  },
  {
    "path": "examples/parallel-buffered-recursive-fibonacci/main.go",
    "chars": 366,
    "preview": "// Command parallel-recursive-fibonacci is a recursive fibonacci which spawns a\n// new goroutine per fib call.\npackage m"
  },
  {
    "path": "examples/parallel-recursive-fibonacci/main.go",
    "chars": 386,
    "preview": "// Command parallel-recursive-fibonacci is a recursive fibonacci which spawns a\n// new goroutine per fib call.\npackage m"
  },
  {
    "path": "examples/parallel-twoprocess-fibonacci/main.go",
    "chars": 451,
    "preview": "// Command parallel-twoprocess-fibonacci is an improved version of parallel\n// fibonacci which limits to only spawning 2"
  },
  {
    "path": "examples/philo/main.go",
    "chars": 388,
    "preview": "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\tfo"
  },
  {
    "path": "examples/popl17/alt-bit/main.go",
    "chars": 1279,
    "preview": "package main\n\n// Alternating bit - from Milner's Communication and Concurrency\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\ttrans "
  },
  {
    "path": "examples/popl17/concsys/main.go",
    "chars": 2683,
    "preview": "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 fak"
  },
  {
    "path": "examples/popl17/cond-recur/main.go",
    "chars": 516,
    "preview": "// Command conditional-recur has a recursion with conditional on one goroutine\n// and another receiving until a done mes"
  },
  {
    "path": "examples/popl17/dinephil/main.go",
    "chars": 931,
    "preview": "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\""
  },
  {
    "path": "examples/popl17/fact/main.go",
    "chars": 244,
    "preview": "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, r"
  },
  {
    "path": "examples/popl17/fanin/main.go",
    "chars": 498,
    "preview": "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 "
  },
  {
    "path": "examples/popl17/fanin-alt/main.go",
    "chars": 632,
    "preview": "package main\n\n// fanin pattern, using for-range loop to consume values (syntactic sugar of\n// loop over r, ok := <-ch)\n\n"
  },
  {
    "path": "examples/popl17/fib/main.go",
    "chars": 386,
    "preview": "// Command parallel-recursive-fibonacci is a recursive fibonacci which spawns a\n// new goroutine per fib call.\npackage m"
  },
  {
    "path": "examples/popl17/fib-async/main.go",
    "chars": 366,
    "preview": "// Command parallel-recursive-fibonacci is a recursive fibonacci which spawns a\n// new goroutine per fib call.\npackage m"
  },
  {
    "path": "examples/popl17/fixed/main.go",
    "chars": 350,
    "preview": "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"
  },
  {
    "path": "examples/popl17/forselect/main.go",
    "chars": 670,
    "preview": "// Command nodet-for-select is a for-select pattern between two compatible\n// recursive select.\npackage main\n\nimport \"fm"
  },
  {
    "path": "examples/popl17/jobsched/main.go",
    "chars": 596,
    "preview": "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\tfo"
  },
  {
    "path": "examples/popl17/mismatch/main.go",
    "chars": 377,
    "preview": "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"
  },
  {
    "path": "examples/popl17/sieve/main.go",
    "chars": 962,
    "preview": "// Command infinite-primesieve is a modified primesieve example from Golang\n// blog. The program generates an infinite l"
  },
  {
    "path": "examples/popl17ae/close/main.go",
    "chars": 63,
    "preview": "package main\n\nfunc main() {\n\tch := make(chan int)\n\tclose(ch)\n}\n"
  },
  {
    "path": "examples/popl17ae/emptyselect/main.go",
    "chars": 131,
    "preview": "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"
  },
  {
    "path": "examples/powsers/powser1.go",
    "chars": 12989,
    "preview": "// run\n\n// +build ignore\n\n// Copyright 2009 The Go Authors. All rights reserved.\n// Use of this source code is governed "
  },
  {
    "path": "examples/producer-consumer/main.go",
    "chars": 370,
    "preview": "package main\n\n// Producer-Consumer example.\n// http://www.golangpatterns.info/concurrency/producer-consumer\n\nimport \"fmt"
  },
  {
    "path": "examples/ring-pattern/main.go",
    "chars": 349,
    "preview": "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"
  },
  {
    "path": "examples/russ-cox-fizzbuzz/main.go",
    "chars": 541,
    "preview": "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 :="
  },
  {
    "path": "examples/select-with-continuation/main.go",
    "chars": 313,
    "preview": "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\ts"
  },
  {
    "path": "examples/select-with-weak-mismatch/main.go",
    "chars": 336,
    "preview": "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() {"
  },
  {
    "path": "examples/semaphores/main.go",
    "chars": 470,
    "preview": "package main\n\n// Semaphores.\n// Emulating semaphores with buffered channels.\n// http://www.golangpatterns.info/concurren"
  },
  {
    "path": "examples/send-recv-with-interfaces/main.go",
    "chars": 307,
    "preview": "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\n"
  },
  {
    "path": "examples/simple/main.go",
    "chars": 127,
    "preview": "package main\n\n// Simplest way of disabling the deadlock detector.\n\nimport _ \"net\"\n\nfunc main() {\n\tch := make(chan int)\n\t"
  },
  {
    "path": "examples/single-gortn-method-call/main.go",
    "chars": 206,
    "preview": "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 i"
  },
  {
    "path": "examples/spawn-in-choice/main.go",
    "chars": 597,
    "preview": "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 4"
  },
  {
    "path": "examples/squaring-cancellation/main.go",
    "chars": 1845,
    "preview": "// Command squaring-cancellation comes from Golang blog to demonstrate fan-in and\n// explicit cancellation. This version"
  },
  {
    "path": "examples/squaring-fanin/main.go",
    "chars": 1868,
    "preview": "// Command squaring-fainin comes from Golang blog to demonstrate fan-in and\n// explicit cancellation. This version uses "
  },
  {
    "path": "examples/squaring-fanin-bad/main.go",
    "chars": 1982,
    "preview": "// Command squaring-fanin-bad comes from Golang blog to demonstrate fan-in and\n// explicit cancellation. This version us"
  },
  {
    "path": "examples/squaring-pipeline/main.go",
    "chars": 1337,
    "preview": "// Command squaring-pipeline comes from Golang blog to demonstrate fan-in and\n// explicit cancellation. This is a modifi"
  },
  {
    "path": "examples/struct-done-channel/main.go",
    "chars": 203,
    "preview": "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() "
  },
  {
    "path": "examples/timeout-behaviour/main.go",
    "chars": 637,
    "preview": "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 ch"
  },
  {
    "path": "fairness/fairness.go",
    "chars": 6177,
    "preview": "// Package fairness runs a fairness analysis.\n//\n// Fairness analysis is an estimation of loop and recursive calls to fi"
  },
  {
    "path": "go.mod",
    "chars": 1177,
    "preview": "module github.com/nickng/dingo-hunter\n\nrequire (\n\tgithub.com/awalterschulze/gographviz v0.0.0-20181013152038-b2885df0431"
  },
  {
    "path": "go.sum",
    "chars": 5996,
    "preview": "github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=\ngithub.com/BurntSushi/toml v0.3.1/go.m"
  },
  {
    "path": "logwriter/logwriter.go",
    "chars": 1563,
    "preview": "// Package logwriter wraps a io.Writer for dingo-hunter logging.\n//\npackage logwriter // \"github.com/nickng/dingo-hunter"
  },
  {
    "path": "main.go",
    "chars": 194,
    "preview": "// +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 := "
  },
  {
    "path": "migoextract/call.go",
    "chars": 11928,
    "preview": "package migoextract\n\n// Functions for handling function call-like instructions\n// i.e. builtin, call, closure, defer, go"
  },
  {
    "path": "migoextract/chan.go",
    "chars": 540,
    "preview": "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\n"
  },
  {
    "path": "migoextract/closure.go",
    "chars": 101,
    "preview": "package migoextract\n\n// Captures are holders for closure capture variables.\ntype Captures []Instance\n"
  },
  {
    "path": "migoextract/commaok.go",
    "chars": 595,
    "preview": "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 diffe"
  },
  {
    "path": "migoextract/context.go",
    "chars": 8872,
    "preview": "package migoextract\n\n// Context captures variables (and invariants) of scopes during execution.\n// Different contexts ar"
  },
  {
    "path": "migoextract/datastructure.go",
    "chars": 1983,
    "preview": "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 ma"
  },
  {
    "path": "migoextract/error.go",
    "chars": 1341,
    "preview": "package migoextract\n\n// Predefined errors\n\nimport \"errors\"\n\nvar (\n\tErrEmptyStack      = errors.New(\"stack: empty\")\n\tErrN"
  },
  {
    "path": "migoextract/instance.go",
    "chars": 2720,
    "preview": "package migoextract\n\n// Wrapper of values and constants in an SSA program.\n// Designed for tracking usage and instances "
  },
  {
    "path": "migoextract/loop.go",
    "chars": 4911,
    "preview": "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/"
  },
  {
    "path": "migoextract/loopbound_string.go",
    "chars": 392,
    "preview": "// generated by stringer -type=LoopBound; DO NOT EDIT\n\npackage migoextract\n\nimport \"fmt\"\n\nconst _LoopBound_name = \"Unkno"
  },
  {
    "path": "migoextract/loopstack.go",
    "chars": 728,
    "preview": "package migoextract\n\nimport (\n\t\"sync\"\n)\n\n// LoopStack is a stack of ssa.BasicBlock\ntype LoopStack struct {\n\tsync.Mutex\n\t"
  },
  {
    "path": "migoextract/loopstate_string.go",
    "chars": 396,
    "preview": "// generated by stringer -type=LoopState; DO NOT EDIT\n\npackage migoextract\n\nimport \"fmt\"\n\nconst _LoopState_name = \"NonLo"
  },
  {
    "path": "migoextract/migoextract.go",
    "chars": 2418,
    "preview": "// Package migoextract provides session type inference from Go code.\n//\npackage migoextract // import \"github.com/nickng"
  },
  {
    "path": "migoextract/phi.go",
    "chars": 2239,
    "preview": "package migoextract\n\n// Deal with Phi nodes.\n\nimport (\n\t\"golang.org/x/tools/go/ssa\"\n)\n\n// phiSelectEdge selects edge bas"
  },
  {
    "path": "migoextract/pointer.go",
    "chars": 509,
    "preview": "package migoextract\n\n// Utility functions for dealing with pointers.\n\nimport (\n\t\"go/types\"\n)\n\n// derefType dereferences "
  },
  {
    "path": "migoextract/print.go",
    "chars": 1450,
    "preview": "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\ncon"
  },
  {
    "path": "migoextract/select.go",
    "chars": 372,
    "preview": "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 sele"
  },
  {
    "path": "migoextract/storage.go",
    "chars": 494,
    "preview": "package migoextract\n\n// Storage is a grouping of auxiliary extra storage.\ntype Storage struct {\n\tarrays  map[Instance]El"
  },
  {
    "path": "migoextract/tuple.go",
    "chars": 161,
    "preview": "package migoextract\n\n// Tuple utils.\n\n// Tuples are return values for function that returns multiple.\n// Can be extracte"
  },
  {
    "path": "migoextract/visit.go",
    "chars": 47625,
    "preview": "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"
  },
  {
    "path": "ssabuilder/callgraph/callgraph.go",
    "chars": 2007,
    "preview": "// Package callgraph represents function call graph.\n//\npackage callgraph // import \"github.com/nickng/dingo-hunter/ssab"
  },
  {
    "path": "ssabuilder/channel.go",
    "chars": 2118,
    "preview": "package ssabuilder\n\n// Channel helper functions.\n// Most of the functions in this file are modified from golan.org/x/too"
  },
  {
    "path": "ssabuilder/errors.go",
    "chars": 112,
    "preview": "package ssabuilder\n\nimport \"errors\"\n\nvar ErrPtaInternal = errors.New(\"internal error: pointer analysis failed\")\n"
  },
  {
    "path": "ssabuilder/pointer.go",
    "chars": 1428,
    "preview": "package ssabuilder\n\n// Pointer analysis helper functions.\n// Most of the functions in this file are modified from golan."
  },
  {
    "path": "ssabuilder/print.go",
    "chars": 1545,
    "preview": "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/"
  },
  {
    "path": "ssabuilder/ssabuild.go",
    "chars": 6143,
    "preview": "// Package ssabuilder provides a wrapper for building SSA IR from Go source code.\n//\npackage ssabuilder // import \"githu"
  },
  {
    "path": "ssabuilder/util.go",
    "chars": 293,
    "preview": "package ssabuilder\n\nimport (\n\t\"golang.org/x/tools/go/ssa\"\n)\n\n// GetMainPkg returns main package of a command.\nfunc MainP"
  },
  {
    "path": "static/script.js",
    "chars": 3955,
    "preview": "// goCode returns string of Go code extracted from #go div.\nfunction goCode() {\n  var code='';\n  $.each($('#go pre'), fu"
  },
  {
    "path": "static/style.css",
    "chars": 3655,
    "preview": "html, body {\n  margin: 0;\n  padding: 0;\n  width: 100%;\n  height: 100%;\n  font-family: 'Roboto', sans-serif;\n}\n\ndiv.gocod"
  },
  {
    "path": "talks/basic/concurrency.go",
    "chars": 334,
    "preview": "// +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.Mill"
  },
  {
    "path": "talks/basic/fanin.go",
    "chars": 460,
    "preview": "// +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, "
  },
  {
    "path": "talks/basic/select.go",
    "chars": 432,
    "preview": "// +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().Uni"
  },
  {
    "path": "talks/conf-cc-2016/deadlock-global.html",
    "chars": 527,
    "preview": "<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 "
  },
  {
    "path": "talks/conf-cc-2016/fanin-global.html",
    "chars": 1152,
    "preview": "<table><tr><td>\n<div class=\"code playground\" contenteditable=\"true\" spellcheck=\"false\">\n<pre style=\"display: none\"><span"
  },
  {
    "path": "talks/conf-cc-2016/select.html",
    "chars": 987,
    "preview": "\n<table><tr><td>\n<div class=\"code playground\" contenteditable=\"true\" spellcheck=\"false\" autocomplete=\"off\">\n<pre style=\""
  },
  {
    "path": "talks/conf-cc-2016/ssa.html",
    "chars": 1009,
    "preview": "<table><tr><td>\n<div class=\"code\" contenteditable=\"true\" spellcheck=\"false\">\n<pre>func main():\n0:                       "
  },
  {
    "path": "talks/conf-cc-2016/type-inference.html",
    "chars": 987,
    "preview": "<table><tr><td>\n<div class=\"code\" contenteditable=\"true\" spellcheck=\"false\">\n<pre><span num=\"17\">func main() {</span>\n<s"
  },
  {
    "path": "talks/conf-cc-2016.slide",
    "chars": 5915,
    "preview": "Static Deadlock Detection for Go by Global Session Graph Synthesis\n\n*Nicholas*Ng* & Nobuko Yoshida\n\nDepartment of Comput"
  },
  {
    "path": "talks/deadlock/deadlock.go",
    "chars": 647,
    "preview": "// +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)                  "
  },
  {
    "path": "talks/snippet/proc.txt",
    "chars": 334,
    "preview": "// From Go source code - https://github.com/golang/go\n// File runtime/proc.go\n\n// +build OMIT\n\nfunc checkdead() {\n...\n\t/"
  },
  {
    "path": "talks/static/article.css",
    "chars": 1897,
    "preview": "body {\n\tmargin: 0;\n\tfont-family: Helvetica, Arial, sans-serif;\n\tfont-size: 16px;\n}\npre,\ncode {\n\tfont-family: Menlo, mono"
  },
  {
    "path": "talks/static/dir.css",
    "chars": 2318,
    "preview": "/* copied from $GOROOT/doc/style.css */\n\nbody {\n\tmargin: 0;\n\tfont-family: Helvetica, Arial, sans-serif;\n\tfont-size: 16px"
  },
  {
    "path": "talks/static/dir.js",
    "chars": 1045,
    "preview": "// Copyright 2012 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license "
  },
  {
    "path": "talks/static/jquery-ui.js",
    "chars": 30584,
    "preview": "/*! jQuery UI - v1.10.2 - 2013-03-20\n* http://jqueryui.com\n* Includes: jquery.ui.core.js, jquery.ui.widget.js, jquery.ui"
  },
  {
    "path": "talks/static/slides.js",
    "chars": 12101,
    "preview": "// Copyright 2012 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license "
  },
  {
    "path": "talks/static/styles.css",
    "chars": 11463,
    "preview": "@media screen {\n  /* Framework */\n  html {\n    height: 100%;\n  }\n\n  body {\n    margin: 0;\n    padding: 0;\n\n    display: "
  },
  {
    "path": "talks/talk-golanguk-2016/calculi.html",
    "chars": 1067,
    "preview": "<div style='padding: 2%'></div>\n<div>\n<div style='display: inline-block; width: 200px;'>\n    <img src='talk-golanguk-201"
  },
  {
    "path": "talks/talk-golanguk-2016/compat.html",
    "chars": 263,
    "preview": "<div class='image'>\n    <div style='display: inline-block'><img src='talk-golanguk-2016/compat.svg'/></div>\n    <div sty"
  },
  {
    "path": "talks/talk-golanguk-2016/datatype.go",
    "chars": 138,
    "preview": "// +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\" /"
  },
  {
    "path": "talks/talk-golanguk-2016/deadlock-notdetected.go",
    "chars": 703,
    "preview": "// +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 <-ch"
  },
  {
    "path": "talks/talk-golanguk-2016/deadlock.go",
    "chars": 595,
    "preview": "// +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 servi"
  },
  {
    "path": "talks/talk-golanguk-2016/main-type.html",
    "chars": 937,
    "preview": "<div>\n<div class=\"code\" contenteditable=\"true\" spellcheck=\"false\" style='display: inline-block; vertical-align: middle'>"
  },
  {
    "path": "talks/talk-golanguk-2016/proc.txt",
    "chars": 254,
    "preview": "// +build OMIT\nfunc checkdead() {\n...\n\t// -1 for sysmon\n\trun := sched.mcount - sched.nmidle - sched.nmidlelocked - 1 // "
  },
  {
    "path": "talks/talk-golanguk-2016/receiver-type.html",
    "chars": 499,
    "preview": "<div>\n<div class=\"code\" contenteditable=\"true\" spellcheck=\"false\" style='display: inline-block; width: 50%; vertical-ali"
  },
  {
    "path": "talks/talk-golanguk-2016/sender-type.html",
    "chars": 468,
    "preview": "<div>\n<div class=\"code\" contenteditable=\"true\" spellcheck=\"false\" style='display: inline-block; width: 50%; vertical-ali"
  },
  {
    "path": "talks/talk-golanguk-2016/video.html",
    "chars": 139,
    "preview": "<iframe width=\"560\" height=\"315\"\n    src=\"https://www.youtube.com/embed/VEGbCjOaMu8?t=1m23s\"\n    frameborder=\"0\" allowfu"
  },
  {
    "path": "talks/talk-golanguk-2016/work-type.html",
    "chars": 639,
    "preview": "<div>\n<div class=\"code\" contenteditable=\"true\" spellcheck=\"false\" style='display: inline-block; width: 50%; vertical-ali"
  },
  {
    "path": "talks/talk-golanguk-2016.slide",
    "chars": 6848,
    "preview": "Static Deadlock Detection for Go\n\nNicholas Ng\n\nImperial College London\n@nicholascwng\nnickng@imperial.ac.uk\n\nhttp://mrg.d"
  },
  {
    "path": "talks/talk-jan-2016.slide",
    "chars": 3833,
    "preview": "Static Deadlock Detection for Go\n\nNicholas Ng / nicholascwng\nImperial College London\n\nhttp://mrg.doc.ic.ac.uk\n\nnickng@im"
  },
  {
    "path": "talks/templates/action.tmpl",
    "chars": 1532,
    "preview": "{/*\nThis is the action template.\nIt determines how the formatting actions are rendered.\n*/}\n\n{{define \"section\"}}\n  <h{{"
  },
  {
    "path": "talks/templates/article.tmpl",
    "chars": 1361,
    "preview": "{/* This is the article template. It defines how articles are formatted. */}\n\n{{define \"root\"}}\n<!DOCTYPE html>\n<html>\n "
  },
  {
    "path": "talks/templates/dir.tmpl",
    "chars": 3253,
    "preview": "<!DOCTYPE html>\n<html>\n<head>\n  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n  <title>Talks - The"
  },
  {
    "path": "talks/templates/slides.tmpl",
    "chars": 1854,
    "preview": "{/* This is the slide template. It defines how presentations are formatted. */}\n\n{{define \"root\"}}\n<!DOCTYPE html>\n<html"
  },
  {
    "path": "templates/index.tmpl",
    "chars": 1917,
    "preview": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"UTF-8\">\n  <title>{{.Title}}</title>\n  <link rel=\"stylesheet\" href=\"http:/"
  },
  {
    "path": "webservice/cfsm.go",
    "chars": 1356,
    "preview": "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"
  },
  {
    "path": "webservice/errors.go",
    "chars": 520,
    "preview": "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 N"
  },
  {
    "path": "webservice/gong.go",
    "chars": 1509,
    "preview": "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\nfun"
  },
  {
    "path": "webservice/handlers.go",
    "chars": 1302,
    "preview": "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 "
  },
  {
    "path": "webservice/migo.go",
    "chars": 1138,
    "preview": "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/migoextr"
  },
  {
    "path": "webservice/play.go",
    "chars": 1213,
    "preview": "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\"t"
  },
  {
    "path": "webservice/server.go",
    "chars": 1361,
    "preview": "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.L"
  },
  {
    "path": "webservice/ssa.go",
    "chars": 556,
    "preview": "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"
  },
  {
    "path": "webservice/synthesis.go",
    "chars": 3804,
    "preview": "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\""
  }
]

About this extraction

This page contains the full source code of the nickng/dingo-hunter GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 171 files (397.9 KB), approximately 126.4k tokens, and a symbol index with 736 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!