build(deps): bump github.com/fsouza/go-dockerclient from 1.8.3 to 1.9.0
Bumps [github.com/fsouza/go-dockerclient](https://github.com/fsouza/go-dockerclient) from 1.8.3 to 1.9.0. - [Release notes](https://github.com/fsouza/go-dockerclient/releases) - [Changelog](https://github.com/fsouza/go-dockerclient/blob/main/container_changes_test.go) - [Commits](https://github.com/fsouza/go-dockerclient/compare/v1.8.3...v1.9.0) --- updated-dependencies: - dependency-name: github.com/fsouza/go-dockerclient dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
This commit is contained in:
		
							parent
							
								
									5cfc0eee32
								
							
						
					
					
						commit
						82431441a9
					
				
							
								
								
									
										8
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										8
									
								
								go.mod
								
								
								
								
							|  | @ -10,9 +10,9 @@ require ( | |||
| 	github.com/containers/ocicrypt v1.1.6 | ||||
| 	github.com/containers/storage v1.43.0 | ||||
| 	github.com/docker/distribution v2.8.1+incompatible | ||||
| 	github.com/docker/docker v20.10.18+incompatible | ||||
| 	github.com/docker/docker v20.10.19+incompatible | ||||
| 	github.com/docker/go-units v0.5.0 | ||||
| 	github.com/fsouza/go-dockerclient v1.8.3 | ||||
| 	github.com/fsouza/go-dockerclient v1.9.0 | ||||
| 	github.com/ghodss/yaml v1.0.0 | ||||
| 	github.com/hashicorp/go-multierror v1.1.1 | ||||
| 	github.com/mattn/go-shellwords v1.0.12 | ||||
|  | @ -41,7 +41,7 @@ require ( | |||
| require ( | ||||
| 	github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect | ||||
| 	github.com/BurntSushi/toml v1.2.0 // indirect | ||||
| 	github.com/Microsoft/go-winio v0.5.2 // indirect | ||||
| 	github.com/Microsoft/go-winio v0.6.0 // indirect | ||||
| 	github.com/Microsoft/hcsshim v0.9.4 // indirect | ||||
| 	github.com/VividCortex/ewma v1.2.0 // indirect | ||||
| 	github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect | ||||
|  | @ -106,8 +106,10 @@ require ( | |||
| 	github.com/xeipuuv/gojsonschema v1.2.0 // indirect | ||||
| 	go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect | ||||
| 	go.opencensus.io v0.23.0 // indirect | ||||
| 	golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect | ||||
| 	golang.org/x/net v0.0.0-20220909164309-bea034e7d591 // indirect | ||||
| 	golang.org/x/text v0.3.7 // indirect | ||||
| 	golang.org/x/tools v0.1.12 // indirect | ||||
| 	google.golang.org/genproto v0.0.0-20220720214146-176da50484ac // indirect | ||||
| 	google.golang.org/grpc v1.48.0 // indirect | ||||
| 	google.golang.org/protobuf v1.28.1 // indirect | ||||
|  |  | |||
							
								
								
									
										15
									
								
								go.sum
								
								
								
								
							
							
						
						
									
										15
									
								
								go.sum
								
								
								
								
							|  | @ -54,8 +54,9 @@ github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JP | |||
| github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= | ||||
| github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= | ||||
| github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= | ||||
| github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= | ||||
| github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= | ||||
| github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= | ||||
| github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= | ||||
| github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= | ||||
| github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= | ||||
| github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= | ||||
|  | @ -298,8 +299,8 @@ github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6 | |||
| github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= | ||||
| github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= | ||||
| github.com/docker/docker v20.10.12+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= | ||||
| github.com/docker/docker v20.10.18+incompatible h1:SN84VYXTBNGn92T/QwIRPlum9zfemfitN7pbsp26WSc= | ||||
| github.com/docker/docker v20.10.18+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= | ||||
| github.com/docker/docker v20.10.19+incompatible h1:lzEmjivyNHFHMNAFLXORMBXyGIhw/UP4DvJwvyKYq64= | ||||
| github.com/docker/docker v20.10.19+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= | ||||
| github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= | ||||
| github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= | ||||
| github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= | ||||
|  | @ -347,8 +348,8 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4 | |||
| github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= | ||||
| github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= | ||||
| github.com/fsouza/go-dockerclient v1.7.7/go.mod h1:njNCXvoZj3sLPjf3yO0DPHf1mdLdCPDYPc14GskKA4Y= | ||||
| github.com/fsouza/go-dockerclient v1.8.3 h1:ckb6sG98D607PNQo012YEhQ8hvuRHx7RISIJM5tjkQM= | ||||
| github.com/fsouza/go-dockerclient v1.8.3/go.mod h1:oenNB8JjNKY4o8I/sug4Qah9si/7OxH4MjL+u7oBxP8= | ||||
| github.com/fsouza/go-dockerclient v1.9.0 h1:KolErz3rXEhwjTuV0VLXcAyB/qFV9bLNZTIHbfpEsOY= | ||||
| github.com/fsouza/go-dockerclient v1.9.0/go.mod h1:OjHFPhuiKyqFDhT5owUV9L7+Ta7GPgnxDWb7UuFKuoQ= | ||||
| github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= | ||||
| github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= | ||||
| github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= | ||||
|  | @ -925,6 +926,8 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB | |||
| golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||||
| golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||||
| golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||||
| 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.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||
| golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||
| golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||
|  | @ -1136,6 +1139,8 @@ golang.org/x/tools v0.0.0-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4X | |||
| golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= | ||||
| golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= | ||||
| golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= | ||||
| golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= | ||||
| golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= | ||||
| golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
|  |  | |||
|  | @ -0,0 +1 @@ | |||
| * text=auto eol=lf | ||||
|  | @ -1 +1,10 @@ | |||
| .vscode/ | ||||
| 
 | ||||
| *.exe | ||||
| 
 | ||||
| # testing | ||||
| testdata | ||||
| 
 | ||||
| # go workspaces | ||||
| go.work | ||||
| go.work.sum | ||||
|  |  | |||
|  | @ -0,0 +1,144 @@ | |||
| run: | ||||
|   skip-dirs: | ||||
|     - pkg/etw/sample | ||||
| 
 | ||||
| linters: | ||||
|   enable: | ||||
|     # style | ||||
|     - containedctx # struct contains a context | ||||
|     - dupl # duplicate code | ||||
|     - errname # erorrs are named correctly | ||||
|     - goconst # strings that should be constants | ||||
|     - godot # comments end in a period | ||||
|     - misspell | ||||
|     - nolintlint # "//nolint" directives are properly explained | ||||
|     - revive # golint replacement | ||||
|     - stylecheck # golint replacement, less configurable than revive | ||||
|     - unconvert # unnecessary conversions | ||||
|     - wastedassign | ||||
| 
 | ||||
|     # bugs, performance, unused, etc ... | ||||
|     - contextcheck # function uses a non-inherited context | ||||
|     - errorlint # errors not wrapped for 1.13 | ||||
|     - exhaustive # check exhaustiveness of enum switch statements | ||||
|     - gofmt # files are gofmt'ed | ||||
|     - gosec # security | ||||
|     - nestif # deeply nested ifs | ||||
|     - nilerr # returns nil even with non-nil error | ||||
|     - prealloc # slices that can be pre-allocated | ||||
|     - structcheck # unused struct fields | ||||
|     - unparam # unused function params | ||||
| 
 | ||||
| issues: | ||||
|   exclude-rules: | ||||
|     # err is very often shadowed in nested scopes | ||||
|     - linters: | ||||
|         - govet | ||||
|       text: '^shadow: declaration of "err" shadows declaration' | ||||
| 
 | ||||
|     # ignore long lines for skip autogen directives | ||||
|     - linters: | ||||
|         - revive | ||||
|       text: "^line-length-limit: " | ||||
|       source: "^//(go:generate|sys) " | ||||
| 
 | ||||
|     # allow unjustified ignores of error checks in defer statements | ||||
|     - linters: | ||||
|         - nolintlint | ||||
|       text: "^directive `//nolint:errcheck` should provide explanation" | ||||
|       source: '^\s*defer ' | ||||
| 
 | ||||
|     # allow unjustified ignores of error lints for io.EOF | ||||
|     - linters: | ||||
|         - nolintlint | ||||
|       text: "^directive `//nolint:errorlint` should provide explanation" | ||||
|       source: '[=|!]= io.EOF' | ||||
| 
 | ||||
| 
 | ||||
| linters-settings: | ||||
|   govet: | ||||
|     enable-all: true | ||||
|     disable: | ||||
|       # struct order is often for Win32 compat | ||||
|       # also, ignore pointer bytes/GC issues for now until performance becomes an issue | ||||
|       - fieldalignment | ||||
|     check-shadowing: true | ||||
|   nolintlint: | ||||
|     allow-leading-space: false | ||||
|     require-explanation: true | ||||
|     require-specific: true | ||||
|   revive: | ||||
|     # revive is more configurable than static check, so likely the preferred alternative to static-check | ||||
|     # (once the perf issue is solved: https://github.com/golangci/golangci-lint/issues/2997) | ||||
|     enable-all-rules: | ||||
|       true | ||||
|       # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md | ||||
|     rules: | ||||
|       # rules with required arguments | ||||
|       - name: argument-limit | ||||
|         disabled: true | ||||
|       - name: banned-characters | ||||
|         disabled: true | ||||
|       - name: cognitive-complexity | ||||
|         disabled: true | ||||
|       - name: cyclomatic | ||||
|         disabled: true | ||||
|       - name: file-header | ||||
|         disabled: true | ||||
|       - name: function-length | ||||
|         disabled: true | ||||
|       - name: function-result-limit | ||||
|         disabled: true | ||||
|       - name: max-public-structs | ||||
|         disabled: true | ||||
|       # geneally annoying rules | ||||
|       - name: add-constant # complains about any and all strings and integers | ||||
|         disabled: true | ||||
|       - name: confusing-naming # we frequently use "Foo()" and "foo()" together | ||||
|         disabled: true | ||||
|       - name: flag-parameter # excessive, and a common idiom we use | ||||
|         disabled: true | ||||
|       # general config | ||||
|       - name: line-length-limit | ||||
|         arguments: | ||||
|           - 140 | ||||
|       - name: var-naming | ||||
|         arguments: | ||||
|           - [] | ||||
|           - - CID | ||||
|             - CRI | ||||
|             - CTRD | ||||
|             - DACL | ||||
|             - DLL | ||||
|             - DOS | ||||
|             - ETW | ||||
|             - FSCTL | ||||
|             - GCS | ||||
|             - GMSA | ||||
|             - HCS | ||||
|             - HV | ||||
|             - IO | ||||
|             - LCOW | ||||
|             - LDAP | ||||
|             - LPAC | ||||
|             - LTSC | ||||
|             - MMIO | ||||
|             - NT | ||||
|             - OCI | ||||
|             - PMEM | ||||
|             - PWSH | ||||
|             - RX | ||||
|             - SACl | ||||
|             - SID | ||||
|             - SMB | ||||
|             - TX | ||||
|             - VHD | ||||
|             - VHDX | ||||
|             - VMID | ||||
|             - VPCI | ||||
|             - WCOW | ||||
|             - WIM | ||||
|   stylecheck: | ||||
|     checks: | ||||
|       - "all" | ||||
|       - "-ST1003" # use revive's var naming | ||||
|  | @ -13,16 +13,60 @@ Please see the LICENSE file for licensing information. | |||
| 
 | ||||
| ## Contributing | ||||
| 
 | ||||
| This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) | ||||
| declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.microsoft.com. | ||||
| This project welcomes contributions and suggestions. | ||||
| Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that | ||||
| you have the right to, and actually do, grant us the rights to use your contribution. | ||||
| For details, visit [Microsoft CLA](https://cla.microsoft.com). | ||||
| 
 | ||||
| When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR | ||||
| appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA. | ||||
| When you submit a pull request, a CLA-bot will automatically determine whether you need to | ||||
| provide a CLA and decorate the PR appropriately (e.g., label, comment). | ||||
| Simply follow the instructions provided by the bot. | ||||
| You will only need to do this once across all repos using our CLA. | ||||
| 
 | ||||
| We also require that contributors sign their commits using git commit -s or git commit --signoff to certify they either authored the work themselves | ||||
| or otherwise have permission to use it in this project. Please see https://developercertificate.org/ for more info, as well as to make sure that you can | ||||
| attest to the rules listed. Our CI uses the DCO Github app to ensure that all commits in a given PR are signed-off. | ||||
| Additionally, the pull request pipeline requires the following steps to be performed before | ||||
| mergining. | ||||
| 
 | ||||
| ### Code Sign-Off | ||||
| 
 | ||||
| We require that contributors sign their commits using [`git commit --signoff`][git-commit-s] | ||||
| to certify they either authored the work themselves or otherwise have permission to use it in this project. | ||||
| 
 | ||||
| A range of commits can be signed off using [`git rebase --signoff`][git-rebase-s]. | ||||
| 
 | ||||
| Please see [the developer certificate](https://developercertificate.org) for more info, | ||||
| as well as to make sure that you can attest to the rules listed. | ||||
| Our CI uses the DCO Github app to ensure that all commits in a given PR are signed-off. | ||||
| 
 | ||||
| ### Linting | ||||
| 
 | ||||
| Code must pass a linting stage, which uses [`golangci-lint`][lint]. | ||||
| The linting settings are stored in [`.golangci.yaml`](./.golangci.yaml), and can be run | ||||
| automatically with VSCode by adding the following to your workspace or folder settings: | ||||
| 
 | ||||
| ```json | ||||
|     "go.lintTool": "golangci-lint", | ||||
|     "go.lintOnSave": "package", | ||||
| ``` | ||||
| 
 | ||||
| Additional editor [integrations options are also available][lint-ide]. | ||||
| 
 | ||||
| Alternatively, `golangci-lint` can be [installed locally][lint-install] and run from the repo root: | ||||
| 
 | ||||
| ```shell | ||||
| # use . or specify a path to only lint a package | ||||
| # to show all lint errors, use flags "--max-issues-per-linter=0 --max-same-issues=0" | ||||
| > golangci-lint run ./... | ||||
| ``` | ||||
| 
 | ||||
| ### Go Generate | ||||
| 
 | ||||
| The pipeline checks that auto-generated code, via `go generate`, are up to date. | ||||
| 
 | ||||
| This can be done for the entire repo: | ||||
| 
 | ||||
| ```shell | ||||
| > go generate ./... | ||||
| ``` | ||||
| 
 | ||||
| ## Code of Conduct | ||||
| 
 | ||||
|  | @ -30,8 +74,16 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://ope | |||
| For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or | ||||
| contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ## Special Thanks | ||||
| Thanks to natefinch for the inspiration for this library. See https://github.com/natefinch/npipe | ||||
| for another named pipe implementation. | ||||
| 
 | ||||
| Thanks to [natefinch][natefinch] for the inspiration for this library. | ||||
| See [npipe](https://github.com/natefinch/npipe) for another named pipe implementation. | ||||
| 
 | ||||
| [lint]: https://golangci-lint.run/ | ||||
| [lint-ide]: https://golangci-lint.run/usage/integrations/#editor-integration | ||||
| [lint-install]: https://golangci-lint.run/usage/install/#local-installation | ||||
| 
 | ||||
| [git-commit-s]: https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--s | ||||
| [git-rebase-s]: https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt---signoff | ||||
| 
 | ||||
| [natefinch]: https://github.com/natefinch | ||||
|  |  | |||
|  | @ -0,0 +1,41 @@ | |||
| <!-- BEGIN MICROSOFT SECURITY.MD V0.0.7 BLOCK --> | ||||
| 
 | ||||
| ## Security | ||||
| 
 | ||||
| Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). | ||||
| 
 | ||||
| If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. | ||||
| 
 | ||||
| ## Reporting Security Issues | ||||
| 
 | ||||
| **Please do not report security vulnerabilities through public GitHub issues.** | ||||
| 
 | ||||
| Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). | ||||
| 
 | ||||
| If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com).  If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). | ||||
| 
 | ||||
| You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc).  | ||||
| 
 | ||||
| Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: | ||||
| 
 | ||||
|   * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) | ||||
|   * Full paths of source file(s) related to the manifestation of the issue | ||||
|   * The location of the affected source code (tag/branch/commit or direct URL) | ||||
|   * Any special configuration required to reproduce the issue | ||||
|   * Step-by-step instructions to reproduce the issue | ||||
|   * Proof-of-concept or exploit code (if possible) | ||||
|   * Impact of the issue, including how an attacker might exploit the issue | ||||
| 
 | ||||
| This information will help us triage your report more quickly. | ||||
| 
 | ||||
| If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. | ||||
| 
 | ||||
| ## Preferred Languages | ||||
| 
 | ||||
| We prefer all communications to be in English. | ||||
| 
 | ||||
| ## Policy | ||||
| 
 | ||||
| Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). | ||||
| 
 | ||||
| <!-- END MICROSOFT SECURITY.MD BLOCK --> | ||||
|  | @ -1,3 +1,4 @@ | |||
| //go:build windows
 | ||||
| // +build windows
 | ||||
| 
 | ||||
| package winio | ||||
|  | @ -7,11 +8,12 @@ import ( | |||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
| 	"runtime" | ||||
| 	"syscall" | ||||
| 	"unicode/utf16" | ||||
| 
 | ||||
| 	"golang.org/x/sys/windows" | ||||
| ) | ||||
| 
 | ||||
| //sys backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupRead
 | ||||
|  | @ -24,7 +26,7 @@ const ( | |||
| 	BackupAlternateData | ||||
| 	BackupLink | ||||
| 	BackupPropertyData | ||||
| 	BackupObjectId | ||||
| 	BackupObjectId //revive:disable-line:var-naming ID, not Id
 | ||||
| 	BackupReparseData | ||||
| 	BackupSparseBlock | ||||
| 	BackupTxfsData | ||||
|  | @ -34,14 +36,16 @@ const ( | |||
| 	StreamSparseAttributes = uint32(8) | ||||
| ) | ||||
| 
 | ||||
| //nolint:revive // var-naming: ALL_CAPS
 | ||||
| const ( | ||||
| 	WRITE_DAC              = 0x40000 | ||||
| 	WRITE_OWNER            = 0x80000 | ||||
| 	ACCESS_SYSTEM_SECURITY = 0x1000000 | ||||
| 	WRITE_DAC              = windows.WRITE_DAC | ||||
| 	WRITE_OWNER            = windows.WRITE_OWNER | ||||
| 	ACCESS_SYSTEM_SECURITY = windows.ACCESS_SYSTEM_SECURITY | ||||
| ) | ||||
| 
 | ||||
| // BackupHeader represents a backup stream of a file.
 | ||||
| type BackupHeader struct { | ||||
| 	//revive:disable-next-line:var-naming ID, not Id
 | ||||
| 	Id         uint32 // The backup stream ID
 | ||||
| 	Attributes uint32 // Stream attributes
 | ||||
| 	Size       int64  // The size of the stream in bytes
 | ||||
|  | @ -49,8 +53,8 @@ type BackupHeader struct { | |||
| 	Offset     int64  // The offset of the stream in the file (for BackupSparseBlock only).
 | ||||
| } | ||||
| 
 | ||||
| type win32StreamId struct { | ||||
| 	StreamId   uint32 | ||||
| type win32StreamID struct { | ||||
| 	StreamID   uint32 | ||||
| 	Attributes uint32 | ||||
| 	Size       uint64 | ||||
| 	NameSize   uint32 | ||||
|  | @ -71,7 +75,7 @@ func NewBackupStreamReader(r io.Reader) *BackupStreamReader { | |||
| // Next returns the next backup stream and prepares for calls to Read(). It skips the remainder of the current stream if
 | ||||
| // it was not completely read.
 | ||||
| func (r *BackupStreamReader) Next() (*BackupHeader, error) { | ||||
| 	if r.bytesLeft > 0 { | ||||
| 	if r.bytesLeft > 0 { //nolint:nestif // todo: flatten this
 | ||||
| 		if s, ok := r.r.(io.Seeker); ok { | ||||
| 			// Make sure Seek on io.SeekCurrent sometimes succeeds
 | ||||
| 			// before trying the actual seek.
 | ||||
|  | @ -82,16 +86,16 @@ func (r *BackupStreamReader) Next() (*BackupHeader, error) { | |||
| 				r.bytesLeft = 0 | ||||
| 			} | ||||
| 		} | ||||
| 		if _, err := io.Copy(ioutil.Discard, r); err != nil { | ||||
| 		if _, err := io.Copy(io.Discard, r); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
| 	var wsi win32StreamId | ||||
| 	var wsi win32StreamID | ||||
| 	if err := binary.Read(r.r, binary.LittleEndian, &wsi); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	hdr := &BackupHeader{ | ||||
| 		Id:         wsi.StreamId, | ||||
| 		Id:         wsi.StreamID, | ||||
| 		Attributes: wsi.Attributes, | ||||
| 		Size:       int64(wsi.Size), | ||||
| 	} | ||||
|  | @ -102,7 +106,7 @@ func (r *BackupStreamReader) Next() (*BackupHeader, error) { | |||
| 		} | ||||
| 		hdr.Name = syscall.UTF16ToString(name) | ||||
| 	} | ||||
| 	if wsi.StreamId == BackupSparseBlock { | ||||
| 	if wsi.StreamID == BackupSparseBlock { | ||||
| 		if err := binary.Read(r.r, binary.LittleEndian, &hdr.Offset); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | @ -147,8 +151,8 @@ func (w *BackupStreamWriter) WriteHeader(hdr *BackupHeader) error { | |||
| 		return fmt.Errorf("missing %d bytes", w.bytesLeft) | ||||
| 	} | ||||
| 	name := utf16.Encode([]rune(hdr.Name)) | ||||
| 	wsi := win32StreamId{ | ||||
| 		StreamId:   hdr.Id, | ||||
| 	wsi := win32StreamID{ | ||||
| 		StreamID:   hdr.Id, | ||||
| 		Attributes: hdr.Attributes, | ||||
| 		Size:       uint64(hdr.Size), | ||||
| 		NameSize:   uint32(len(name) * 2), | ||||
|  | @ -203,7 +207,7 @@ func (r *BackupFileReader) Read(b []byte) (int, error) { | |||
| 	var bytesRead uint32 | ||||
| 	err := backupRead(syscall.Handle(r.f.Fd()), b, &bytesRead, false, r.includeSecurity, &r.ctx) | ||||
| 	if err != nil { | ||||
| 		return 0, &os.PathError{"BackupRead", r.f.Name(), err} | ||||
| 		return 0, &os.PathError{Op: "BackupRead", Path: r.f.Name(), Err: err} | ||||
| 	} | ||||
| 	runtime.KeepAlive(r.f) | ||||
| 	if bytesRead == 0 { | ||||
|  | @ -216,7 +220,7 @@ func (r *BackupFileReader) Read(b []byte) (int, error) { | |||
| // the underlying file.
 | ||||
| func (r *BackupFileReader) Close() error { | ||||
| 	if r.ctx != 0 { | ||||
| 		backupRead(syscall.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx) | ||||
| 		_ = backupRead(syscall.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx) | ||||
| 		runtime.KeepAlive(r.f) | ||||
| 		r.ctx = 0 | ||||
| 	} | ||||
|  | @ -242,7 +246,7 @@ func (w *BackupFileWriter) Write(b []byte) (int, error) { | |||
| 	var bytesWritten uint32 | ||||
| 	err := backupWrite(syscall.Handle(w.f.Fd()), b, &bytesWritten, false, w.includeSecurity, &w.ctx) | ||||
| 	if err != nil { | ||||
| 		return 0, &os.PathError{"BackupWrite", w.f.Name(), err} | ||||
| 		return 0, &os.PathError{Op: "BackupWrite", Path: w.f.Name(), Err: err} | ||||
| 	} | ||||
| 	runtime.KeepAlive(w.f) | ||||
| 	if int(bytesWritten) != len(b) { | ||||
|  | @ -255,7 +259,7 @@ func (w *BackupFileWriter) Write(b []byte) (int, error) { | |||
| // close the underlying file.
 | ||||
| func (w *BackupFileWriter) Close() error { | ||||
| 	if w.ctx != 0 { | ||||
| 		backupWrite(syscall.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx) | ||||
| 		_ = backupWrite(syscall.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx) | ||||
| 		runtime.KeepAlive(w.f) | ||||
| 		w.ctx = 0 | ||||
| 	} | ||||
|  | @ -271,7 +275,13 @@ func OpenForBackup(path string, access uint32, share uint32, createmode uint32) | |||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	h, err := syscall.CreateFile(&winPath[0], access, share, nil, createmode, syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OPEN_REPARSE_POINT, 0) | ||||
| 	h, err := syscall.CreateFile(&winPath[0], | ||||
| 		access, | ||||
| 		share, | ||||
| 		nil, | ||||
| 		createmode, | ||||
| 		syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OPEN_REPARSE_POINT, | ||||
| 		0) | ||||
| 	if err != nil { | ||||
| 		err = &os.PathError{Op: "open", Path: path, Err: err} | ||||
| 		return nil, err | ||||
|  |  | |||
|  | @ -1,4 +1,3 @@ | |||
| // +build !windows
 | ||||
| // This file only exists to allow go get on non-Windows platforms.
 | ||||
| 
 | ||||
| package backuptar | ||||
|  | @ -1,3 +1,5 @@ | |||
| //go:build windows
 | ||||
| 
 | ||||
| package backuptar | ||||
| 
 | ||||
| import ( | ||||
|  |  | |||
|  | @ -1,3 +1,4 @@ | |||
| //go:build windows
 | ||||
| // +build windows
 | ||||
| 
 | ||||
| package backuptar | ||||
|  | @ -7,7 +8,6 @@ import ( | |||
| 	"encoding/base64" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"path/filepath" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
|  | @ -18,17 +18,18 @@ import ( | |||
| 	"golang.org/x/sys/windows" | ||||
| ) | ||||
| 
 | ||||
| //nolint:deadcode,varcheck // keep unused constants for potential future use
 | ||||
| const ( | ||||
| 	c_ISUID  = 04000   // Set uid
 | ||||
| 	c_ISGID  = 02000   // Set gid
 | ||||
| 	c_ISVTX  = 01000   // Save text (sticky bit)
 | ||||
| 	c_ISDIR  = 040000  // Directory
 | ||||
| 	c_ISFIFO = 010000  // FIFO
 | ||||
| 	c_ISREG  = 0100000 // Regular file
 | ||||
| 	c_ISLNK  = 0120000 // Symbolic link
 | ||||
| 	c_ISBLK  = 060000  // Block special file
 | ||||
| 	c_ISCHR  = 020000  // Character special file
 | ||||
| 	c_ISSOCK = 0140000 // Socket
 | ||||
| 	cISUID  = 0004000 // Set uid
 | ||||
| 	cISGID  = 0002000 // Set gid
 | ||||
| 	cISVTX  = 0001000 // Save text (sticky bit)
 | ||||
| 	cISDIR  = 0040000 // Directory
 | ||||
| 	cISFIFO = 0010000 // FIFO
 | ||||
| 	cISREG  = 0100000 // Regular file
 | ||||
| 	cISLNK  = 0120000 // Symbolic link
 | ||||
| 	cISBLK  = 0060000 // Block special file
 | ||||
| 	cISCHR  = 0020000 // Character special file
 | ||||
| 	cISSOCK = 0140000 // Socket
 | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
|  | @ -44,7 +45,7 @@ const ( | |||
| // zeroReader is an io.Reader that always returns 0s.
 | ||||
| type zeroReader struct{} | ||||
| 
 | ||||
| func (zr zeroReader) Read(b []byte) (int, error) { | ||||
| func (zeroReader) Read(b []byte) (int, error) { | ||||
| 	for i := range b { | ||||
| 		b[i] = 0 | ||||
| 	} | ||||
|  | @ -55,7 +56,7 @@ func copySparse(t *tar.Writer, br *winio.BackupStreamReader) error { | |||
| 	curOffset := int64(0) | ||||
| 	for { | ||||
| 		bhdr, err := br.Next() | ||||
| 		if err == io.EOF { | ||||
| 		if err == io.EOF { //nolint:errorlint
 | ||||
| 			err = io.ErrUnexpectedEOF | ||||
| 		} | ||||
| 		if err != nil { | ||||
|  | @ -71,8 +72,8 @@ func copySparse(t *tar.Writer, br *winio.BackupStreamReader) error { | |||
| 		} | ||||
| 		// archive/tar does not support writing sparse files
 | ||||
| 		// so just write zeroes to catch up to the current offset.
 | ||||
| 		if _, err := io.CopyN(t, zeroReader{}, bhdr.Offset-curOffset); err != nil { | ||||
| 			return fmt.Errorf("seek to offset %d: %s", bhdr.Offset, err) | ||||
| 		if _, err = io.CopyN(t, zeroReader{}, bhdr.Offset-curOffset); err != nil { | ||||
| 			return fmt.Errorf("seek to offset %d: %w", bhdr.Offset, err) | ||||
| 		} | ||||
| 		if bhdr.Size == 0 { | ||||
| 			// A sparse block with size = 0 is used to mark the end of the sparse blocks.
 | ||||
|  | @ -106,7 +107,7 @@ func BasicInfoHeader(name string, size int64, fileInfo *winio.FileBasicInfo) *ta | |||
| 	hdr.PAXRecords[hdrCreationTime] = formatPAXTime(time.Unix(0, fileInfo.CreationTime.Nanoseconds())) | ||||
| 
 | ||||
| 	if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 { | ||||
| 		hdr.Mode |= c_ISDIR | ||||
| 		hdr.Mode |= cISDIR | ||||
| 		hdr.Size = 0 | ||||
| 		hdr.Typeflag = tar.TypeDir | ||||
| 	} | ||||
|  | @ -116,32 +117,29 @@ func BasicInfoHeader(name string, size int64, fileInfo *winio.FileBasicInfo) *ta | |||
| // SecurityDescriptorFromTarHeader reads the SDDL associated with the header of the current file
 | ||||
| // from the tar header and returns the security descriptor into a byte slice.
 | ||||
| func SecurityDescriptorFromTarHeader(hdr *tar.Header) ([]byte, error) { | ||||
| 	// Maintaining old SDDL-based behavior for backward
 | ||||
| 	// compatibility.  All new tar headers written by this library
 | ||||
| 	// will have raw binary for the security descriptor.
 | ||||
| 	var sd []byte | ||||
| 	var err error | ||||
| 	if sddl, ok := hdr.PAXRecords[hdrSecurityDescriptor]; ok { | ||||
| 		sd, err = winio.SddlToSecurityDescriptor(sddl) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
| 	if sdraw, ok := hdr.PAXRecords[hdrRawSecurityDescriptor]; ok { | ||||
| 		sd, err = base64.StdEncoding.DecodeString(sdraw) | ||||
| 		sd, err := base64.StdEncoding.DecodeString(sdraw) | ||||
| 		if err != nil { | ||||
| 			// Not returning sd as-is in the error-case, as base64.DecodeString
 | ||||
| 			// may return partially decoded data (not nil or empty slice) in case
 | ||||
| 			// of a failure: https://github.com/golang/go/blob/go1.17.7/src/encoding/base64/base64.go#L382-L387
 | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		return sd, nil | ||||
| 	} | ||||
| 	return sd, nil | ||||
| 	// Maintaining old SDDL-based behavior for backward compatibility. All new
 | ||||
| 	// tar headers written by this library will have raw binary for the security
 | ||||
| 	// descriptor.
 | ||||
| 	if sddl, ok := hdr.PAXRecords[hdrSecurityDescriptor]; ok { | ||||
| 		return winio.SddlToSecurityDescriptor(sddl) | ||||
| 	} | ||||
| 	return nil, nil | ||||
| } | ||||
| 
 | ||||
| // ExtendedAttributesFromTarHeader reads the EAs associated with the header of the
 | ||||
| // current file from the tar header and returns it as a byte slice.
 | ||||
| func ExtendedAttributesFromTarHeader(hdr *tar.Header) ([]byte, error) { | ||||
| 	var eas []winio.ExtendedAttribute | ||||
| 	var eadata []byte | ||||
| 	var err error | ||||
| 	var eas []winio.ExtendedAttribute //nolint:prealloc // len(eas) <= len(hdr.PAXRecords); prealloc is wasteful
 | ||||
| 	for k, v := range hdr.PAXRecords { | ||||
| 		if !strings.HasPrefix(k, hdrEaPrefix) { | ||||
| 			continue | ||||
|  | @ -155,13 +153,15 @@ func ExtendedAttributesFromTarHeader(hdr *tar.Header) ([]byte, error) { | |||
| 			Value: data, | ||||
| 		}) | ||||
| 	} | ||||
| 	var eaData []byte | ||||
| 	var err error | ||||
| 	if len(eas) != 0 { | ||||
| 		eadata, err = winio.EncodeExtendedAttributes(eas) | ||||
| 		eaData, err = winio.EncodeExtendedAttributes(eas) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
| 	return eadata, nil | ||||
| 	return eaData, nil | ||||
| } | ||||
| 
 | ||||
| // EncodeReparsePointFromTarHeader reads the ReparsePoint structure from the tar header
 | ||||
|  | @ -182,11 +182,9 @@ func EncodeReparsePointFromTarHeader(hdr *tar.Header) []byte { | |||
| //
 | ||||
| // The additional Win32 metadata is:
 | ||||
| //
 | ||||
| // MSWINDOWS.fileattr: The Win32 file attributes, as a decimal value
 | ||||
| //
 | ||||
| // MSWINDOWS.rawsd: The Win32 security descriptor, in raw binary format
 | ||||
| //
 | ||||
| // MSWINDOWS.mountpoint: If present, this is a mount point and not a symlink, even though the type is '2' (symlink)
 | ||||
| //   - MSWINDOWS.fileattr: The Win32 file attributes, as a decimal value
 | ||||
| //   - MSWINDOWS.rawsd: The Win32 security descriptor, in raw binary format
 | ||||
| //   - MSWINDOWS.mountpoint: If present, this is a mount point and not a symlink, even though the type is '2' (symlink)
 | ||||
| func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size int64, fileInfo *winio.FileBasicInfo) error { | ||||
| 	name = filepath.ToSlash(name) | ||||
| 	hdr := BasicInfoHeader(name, size, fileInfo) | ||||
|  | @ -209,7 +207,7 @@ func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size | |||
| 	var dataHdr *winio.BackupHeader | ||||
| 	for dataHdr == nil { | ||||
| 		bhdr, err := br.Next() | ||||
| 		if err == io.EOF { | ||||
| 		if err == io.EOF { //nolint:errorlint
 | ||||
| 			break | ||||
| 		} | ||||
| 		if err != nil { | ||||
|  | @ -217,21 +215,21 @@ func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size | |||
| 		} | ||||
| 		switch bhdr.Id { | ||||
| 		case winio.BackupData: | ||||
| 			hdr.Mode |= c_ISREG | ||||
| 			hdr.Mode |= cISREG | ||||
| 			if !readTwice { | ||||
| 				dataHdr = bhdr | ||||
| 			} | ||||
| 		case winio.BackupSecurity: | ||||
| 			sd, err := ioutil.ReadAll(br) | ||||
| 			sd, err := io.ReadAll(br) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			hdr.PAXRecords[hdrRawSecurityDescriptor] = base64.StdEncoding.EncodeToString(sd) | ||||
| 
 | ||||
| 		case winio.BackupReparseData: | ||||
| 			hdr.Mode |= c_ISLNK | ||||
| 			hdr.Mode |= cISLNK | ||||
| 			hdr.Typeflag = tar.TypeSymlink | ||||
| 			reparseBuffer, err := ioutil.ReadAll(br) | ||||
| 			reparseBuffer, _ := io.ReadAll(br) | ||||
| 			rp, err := winio.DecodeReparsePoint(reparseBuffer) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
|  | @ -242,7 +240,7 @@ func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size | |||
| 			hdr.Linkname = rp.Target | ||||
| 
 | ||||
| 		case winio.BackupEaData: | ||||
| 			eab, err := ioutil.ReadAll(br) | ||||
| 			eab, err := io.ReadAll(br) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
|  | @ -276,7 +274,7 @@ func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size | |||
| 		} | ||||
| 		for dataHdr == nil { | ||||
| 			bhdr, err := br.Next() | ||||
| 			if err == io.EOF { | ||||
| 			if err == io.EOF { //nolint:errorlint
 | ||||
| 				break | ||||
| 			} | ||||
| 			if err != nil { | ||||
|  | @ -311,7 +309,7 @@ func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size | |||
| 	//     range of the file containing the range contents. Finally there is a sparse block stream with
 | ||||
| 	//     size = 0 and offset = <file size>.
 | ||||
| 
 | ||||
| 	if dataHdr != nil { | ||||
| 	if dataHdr != nil { //nolint:nestif // todo: reduce nesting complexity
 | ||||
| 		// A data stream was found. Copy the data.
 | ||||
| 		// We assume that we will either have a data stream size > 0 XOR have sparse block streams.
 | ||||
| 		if dataHdr.Size > 0 || (dataHdr.Attributes&winio.StreamSparseAttributes) == 0 { | ||||
|  | @ -319,13 +317,13 @@ func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size | |||
| 				return fmt.Errorf("%s: mismatch between file size %d and header size %d", name, size, dataHdr.Size) | ||||
| 			} | ||||
| 			if _, err = io.Copy(t, br); err != nil { | ||||
| 				return fmt.Errorf("%s: copying contents from data stream: %s", name, err) | ||||
| 				return fmt.Errorf("%s: copying contents from data stream: %w", name, err) | ||||
| 			} | ||||
| 		} else if size > 0 { | ||||
| 			// As of a recent OS change, BackupRead now returns a data stream for empty sparse files.
 | ||||
| 			// These files have no sparse block streams, so skip the copySparse call if file size = 0.
 | ||||
| 			if err = copySparse(t, br); err != nil { | ||||
| 				return fmt.Errorf("%s: copying contents from sparse block stream: %s", name, err) | ||||
| 				return fmt.Errorf("%s: copying contents from sparse block stream: %w", name, err) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | @ -335,7 +333,7 @@ func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size | |||
| 	// been written. In practice, this means that we don't get EA or TXF metadata.
 | ||||
| 	for { | ||||
| 		bhdr, err := br.Next() | ||||
| 		if err == io.EOF { | ||||
| 		if err == io.EOF { //nolint:errorlint
 | ||||
| 			break | ||||
| 		} | ||||
| 		if err != nil { | ||||
|  | @ -343,35 +341,30 @@ func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size | |||
| 		} | ||||
| 		switch bhdr.Id { | ||||
| 		case winio.BackupAlternateData: | ||||
| 			altName := bhdr.Name | ||||
| 			if strings.HasSuffix(altName, ":$DATA") { | ||||
| 				altName = altName[:len(altName)-len(":$DATA")] | ||||
| 			} | ||||
| 			if (bhdr.Attributes & winio.StreamSparseAttributes) == 0 { | ||||
| 				hdr = &tar.Header{ | ||||
| 					Format:     hdr.Format, | ||||
| 					Name:       name + altName, | ||||
| 					Mode:       hdr.Mode, | ||||
| 					Typeflag:   tar.TypeReg, | ||||
| 					Size:       bhdr.Size, | ||||
| 					ModTime:    hdr.ModTime, | ||||
| 					AccessTime: hdr.AccessTime, | ||||
| 					ChangeTime: hdr.ChangeTime, | ||||
| 				} | ||||
| 				err = t.WriteHeader(hdr) | ||||
| 				if err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| 				_, err = io.Copy(t, br) | ||||
| 				if err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| 
 | ||||
| 			} else { | ||||
| 			if (bhdr.Attributes & winio.StreamSparseAttributes) != 0 { | ||||
| 				// Unsupported for now, since the size of the alternate stream is not present
 | ||||
| 				// in the backup stream until after the data has been read.
 | ||||
| 				return fmt.Errorf("%s: tar of sparse alternate data streams is unsupported", name) | ||||
| 			} | ||||
| 			altName := strings.TrimSuffix(bhdr.Name, ":$DATA") | ||||
| 			hdr = &tar.Header{ | ||||
| 				Format:     hdr.Format, | ||||
| 				Name:       name + altName, | ||||
| 				Mode:       hdr.Mode, | ||||
| 				Typeflag:   tar.TypeReg, | ||||
| 				Size:       bhdr.Size, | ||||
| 				ModTime:    hdr.ModTime, | ||||
| 				AccessTime: hdr.AccessTime, | ||||
| 				ChangeTime: hdr.ChangeTime, | ||||
| 			} | ||||
| 			err = t.WriteHeader(hdr) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			_, err = io.Copy(t, br) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		case winio.BackupEaData, winio.BackupLink, winio.BackupPropertyData, winio.BackupObjectId, winio.BackupTxfsData: | ||||
| 			// ignore these streams
 | ||||
| 		default: | ||||
|  | @ -413,7 +406,7 @@ func FileInfoFromHeader(hdr *tar.Header) (name string, size int64, fileInfo *win | |||
| 		} | ||||
| 		fileInfo.CreationTime = windows.NsecToFiletime(creationTime.UnixNano()) | ||||
| 	} | ||||
| 	return | ||||
| 	return name, size, fileInfo, err | ||||
| } | ||||
| 
 | ||||
| // WriteBackupStreamFromTarFile writes a Win32 backup stream from the current tar file. Since this function may process multiple
 | ||||
|  | @ -474,7 +467,6 @@ func WriteBackupStreamFromTarFile(w io.Writer, t *tar.Reader, hdr *tar.Header) ( | |||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	if hdr.Typeflag == tar.TypeReg || hdr.Typeflag == tar.TypeRegA { | ||||
|  |  | |||
|  | @ -0,0 +1,22 @@ | |||
| // This package provides utilities for efficiently performing Win32 IO operations in Go.
 | ||||
| // Currently, this package is provides support for genreal IO and management of
 | ||||
| //   - named pipes
 | ||||
| //   - files
 | ||||
| //   - [Hyper-V sockets]
 | ||||
| //
 | ||||
| // This code is similar to Go's [net] package, and uses IO completion ports to avoid
 | ||||
| // blocking IO on system threads, allowing Go to reuse the thread to schedule other goroutines.
 | ||||
| //
 | ||||
| // This limits support to Windows Vista and newer operating systems.
 | ||||
| //
 | ||||
| // Additionally, this package provides support for:
 | ||||
| //   - creating and managing GUIDs
 | ||||
| //   - writing to [ETW]
 | ||||
| //   - opening and manageing VHDs
 | ||||
| //   - parsing [Windows Image files]
 | ||||
| //   - auto-generating Win32 API code
 | ||||
| //
 | ||||
| // [Hyper-V sockets]: https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/user-guide/make-integration-service
 | ||||
| // [ETW]: https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/event-tracing-for-windows--etw-
 | ||||
| // [Windows Image files]: https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/work-with-windows-images
 | ||||
| package winio | ||||
|  | @ -33,7 +33,7 @@ func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) { | |||
| 	err = binary.Read(bytes.NewReader(b), binary.LittleEndian, &info) | ||||
| 	if err != nil { | ||||
| 		err = errInvalidEaBuffer | ||||
| 		return | ||||
| 		return ea, nb, err | ||||
| 	} | ||||
| 
 | ||||
| 	nameOffset := fileFullEaInformationSize | ||||
|  | @ -43,7 +43,7 @@ func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) { | |||
| 	nextOffset := int(info.NextEntryOffset) | ||||
| 	if valueLen+valueOffset > len(b) || nextOffset < 0 || nextOffset > len(b) { | ||||
| 		err = errInvalidEaBuffer | ||||
| 		return | ||||
| 		return ea, nb, err | ||||
| 	} | ||||
| 
 | ||||
| 	ea.Name = string(b[nameOffset : nameOffset+nameLen]) | ||||
|  | @ -52,7 +52,7 @@ func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) { | |||
| 	if info.NextEntryOffset != 0 { | ||||
| 		nb = b[info.NextEntryOffset:] | ||||
| 	} | ||||
| 	return | ||||
| 	return ea, nb, err | ||||
| } | ||||
| 
 | ||||
| // DecodeExtendedAttributes decodes a list of EAs from a FILE_FULL_EA_INFORMATION
 | ||||
|  | @ -67,7 +67,7 @@ func DecodeExtendedAttributes(b []byte) (eas []ExtendedAttribute, err error) { | |||
| 		eas = append(eas, ea) | ||||
| 		b = nb | ||||
| 	} | ||||
| 	return | ||||
| 	return eas, err | ||||
| } | ||||
| 
 | ||||
| func writeEa(buf *bytes.Buffer, ea *ExtendedAttribute, last bool) error { | ||||
|  |  | |||
|  | @ -11,6 +11,8 @@ import ( | |||
| 	"sync/atomic" | ||||
| 	"syscall" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"golang.org/x/sys/windows" | ||||
| ) | ||||
| 
 | ||||
| //sys cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) = CancelIoEx
 | ||||
|  | @ -24,6 +26,8 @@ type atomicBool int32 | |||
| func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 } | ||||
| func (b *atomicBool) setFalse()   { atomic.StoreInt32((*int32)(b), 0) } | ||||
| func (b *atomicBool) setTrue()    { atomic.StoreInt32((*int32)(b), 1) } | ||||
| 
 | ||||
| //revive:disable-next-line:predeclared Keep "new" to maintain consistency with "atomic" pkg
 | ||||
| func (b *atomicBool) swap(new bool) bool { | ||||
| 	var newInt int32 | ||||
| 	if new { | ||||
|  | @ -32,11 +36,6 @@ func (b *atomicBool) swap(new bool) bool { | |||
| 	return atomic.SwapInt32((*int32)(b), newInt) == 1 | ||||
| } | ||||
| 
 | ||||
| const ( | ||||
| 	cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 1 | ||||
| 	cFILE_SKIP_SET_EVENT_ON_HANDLE        = 2 | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	ErrFileClosed = errors.New("file has already been closed") | ||||
| 	ErrTimeout    = &timeoutError{} | ||||
|  | @ -44,28 +43,28 @@ var ( | |||
| 
 | ||||
| type timeoutError struct{} | ||||
| 
 | ||||
| func (e *timeoutError) Error() string   { return "i/o timeout" } | ||||
| func (e *timeoutError) Timeout() bool   { return true } | ||||
| func (e *timeoutError) Temporary() bool { return true } | ||||
| func (*timeoutError) Error() string   { return "i/o timeout" } | ||||
| func (*timeoutError) Timeout() bool   { return true } | ||||
| func (*timeoutError) Temporary() bool { return true } | ||||
| 
 | ||||
| type timeoutChan chan struct{} | ||||
| 
 | ||||
| var ioInitOnce sync.Once | ||||
| var ioCompletionPort syscall.Handle | ||||
| 
 | ||||
| // ioResult contains the result of an asynchronous IO operation
 | ||||
| // ioResult contains the result of an asynchronous IO operation.
 | ||||
| type ioResult struct { | ||||
| 	bytes uint32 | ||||
| 	err   error | ||||
| } | ||||
| 
 | ||||
| // ioOperation represents an outstanding asynchronous Win32 IO
 | ||||
| // ioOperation represents an outstanding asynchronous Win32 IO.
 | ||||
| type ioOperation struct { | ||||
| 	o  syscall.Overlapped | ||||
| 	ch chan ioResult | ||||
| } | ||||
| 
 | ||||
| func initIo() { | ||||
| func initIO() { | ||||
| 	h, err := createIoCompletionPort(syscall.InvalidHandle, 0, 0, 0xffffffff) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
|  | @ -94,15 +93,15 @@ type deadlineHandler struct { | |||
| 	timedout    atomicBool | ||||
| } | ||||
| 
 | ||||
| // makeWin32File makes a new win32File from an existing file handle
 | ||||
| // makeWin32File makes a new win32File from an existing file handle.
 | ||||
| func makeWin32File(h syscall.Handle) (*win32File, error) { | ||||
| 	f := &win32File{handle: h} | ||||
| 	ioInitOnce.Do(initIo) | ||||
| 	ioInitOnce.Do(initIO) | ||||
| 	_, err := createIoCompletionPort(h, ioCompletionPort, 0, 0xffffffff) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	err = setFileCompletionNotificationModes(h, cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS|cFILE_SKIP_SET_EVENT_ON_HANDLE) | ||||
| 	err = setFileCompletionNotificationModes(h, windows.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS|windows.FILE_SKIP_SET_EVENT_ON_HANDLE) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | @ -121,14 +120,14 @@ func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) { | |||
| 	return f, nil | ||||
| } | ||||
| 
 | ||||
| // closeHandle closes the resources associated with a Win32 handle
 | ||||
| // closeHandle closes the resources associated with a Win32 handle.
 | ||||
| func (f *win32File) closeHandle() { | ||||
| 	f.wgLock.Lock() | ||||
| 	// Atomically set that we are closing, releasing the resources only once.
 | ||||
| 	if !f.closing.swap(true) { | ||||
| 		f.wgLock.Unlock() | ||||
| 		// cancel all IO and wait for it to complete
 | ||||
| 		cancelIoEx(f.handle, nil) | ||||
| 		_ = cancelIoEx(f.handle, nil) | ||||
| 		f.wg.Wait() | ||||
| 		// at this point, no new IO can start
 | ||||
| 		syscall.Close(f.handle) | ||||
|  | @ -144,14 +143,14 @@ func (f *win32File) Close() error { | |||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // IsClosed checks if the file has been closed
 | ||||
| // IsClosed checks if the file has been closed.
 | ||||
| func (f *win32File) IsClosed() bool { | ||||
| 	return f.closing.isSet() | ||||
| } | ||||
| 
 | ||||
| // prepareIo prepares for a new IO operation.
 | ||||
| // prepareIO prepares for a new IO operation.
 | ||||
| // The caller must call f.wg.Done() when the IO is finished, prior to Close() returning.
 | ||||
| func (f *win32File) prepareIo() (*ioOperation, error) { | ||||
| func (f *win32File) prepareIO() (*ioOperation, error) { | ||||
| 	f.wgLock.RLock() | ||||
| 	if f.closing.isSet() { | ||||
| 		f.wgLock.RUnlock() | ||||
|  | @ -164,7 +163,7 @@ func (f *win32File) prepareIo() (*ioOperation, error) { | |||
| 	return c, nil | ||||
| } | ||||
| 
 | ||||
| // ioCompletionProcessor processes completed async IOs forever
 | ||||
| // ioCompletionProcessor processes completed async IOs forever.
 | ||||
| func ioCompletionProcessor(h syscall.Handle) { | ||||
| 	for { | ||||
| 		var bytes uint32 | ||||
|  | @ -178,15 +177,17 @@ func ioCompletionProcessor(h syscall.Handle) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| // asyncIo processes the return value from ReadFile or WriteFile, blocking until
 | ||||
| // todo: helsaawy - create an asyncIO version that takes a context
 | ||||
| 
 | ||||
| // asyncIO processes the return value from ReadFile or WriteFile, blocking until
 | ||||
| // the operation has actually completed.
 | ||||
| func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) { | ||||
| 	if err != syscall.ERROR_IO_PENDING { | ||||
| func (f *win32File) asyncIO(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) { | ||||
| 	if err != syscall.ERROR_IO_PENDING { //nolint:errorlint // err is Errno
 | ||||
| 		return int(bytes), err | ||||
| 	} | ||||
| 
 | ||||
| 	if f.closing.isSet() { | ||||
| 		cancelIoEx(f.handle, &c.o) | ||||
| 		_ = cancelIoEx(f.handle, &c.o) | ||||
| 	} | ||||
| 
 | ||||
| 	var timeout timeoutChan | ||||
|  | @ -200,7 +201,7 @@ func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, er | |||
| 	select { | ||||
| 	case r = <-c.ch: | ||||
| 		err = r.err | ||||
| 		if err == syscall.ERROR_OPERATION_ABORTED { | ||||
| 		if err == syscall.ERROR_OPERATION_ABORTED { //nolint:errorlint // err is Errno
 | ||||
| 			if f.closing.isSet() { | ||||
| 				err = ErrFileClosed | ||||
| 			} | ||||
|  | @ -210,10 +211,10 @@ func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, er | |||
| 			err = wsaGetOverlappedResult(f.handle, &c.o, &bytes, false, &flags) | ||||
| 		} | ||||
| 	case <-timeout: | ||||
| 		cancelIoEx(f.handle, &c.o) | ||||
| 		_ = cancelIoEx(f.handle, &c.o) | ||||
| 		r = <-c.ch | ||||
| 		err = r.err | ||||
| 		if err == syscall.ERROR_OPERATION_ABORTED { | ||||
| 		if err == syscall.ERROR_OPERATION_ABORTED { //nolint:errorlint // err is Errno
 | ||||
| 			err = ErrTimeout | ||||
| 		} | ||||
| 	} | ||||
|  | @ -221,13 +222,14 @@ func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, er | |||
| 	// runtime.KeepAlive is needed, as c is passed via native
 | ||||
| 	// code to ioCompletionProcessor, c must remain alive
 | ||||
| 	// until the channel read is complete.
 | ||||
| 	// todo: (de)allocate *ioOperation via win32 heap functions, instead of needing to KeepAlive?
 | ||||
| 	runtime.KeepAlive(c) | ||||
| 	return int(r.bytes), err | ||||
| } | ||||
| 
 | ||||
| // Read reads from a file handle.
 | ||||
| func (f *win32File) Read(b []byte) (int, error) { | ||||
| 	c, err := f.prepareIo() | ||||
| 	c, err := f.prepareIO() | ||||
| 	if err != nil { | ||||
| 		return 0, err | ||||
| 	} | ||||
|  | @ -239,13 +241,13 @@ func (f *win32File) Read(b []byte) (int, error) { | |||
| 
 | ||||
| 	var bytes uint32 | ||||
| 	err = syscall.ReadFile(f.handle, b, &bytes, &c.o) | ||||
| 	n, err := f.asyncIo(c, &f.readDeadline, bytes, err) | ||||
| 	n, err := f.asyncIO(c, &f.readDeadline, bytes, err) | ||||
| 	runtime.KeepAlive(b) | ||||
| 
 | ||||
| 	// Handle EOF conditions.
 | ||||
| 	if err == nil && n == 0 && len(b) != 0 { | ||||
| 		return 0, io.EOF | ||||
| 	} else if err == syscall.ERROR_BROKEN_PIPE { | ||||
| 	} else if err == syscall.ERROR_BROKEN_PIPE { //nolint:errorlint // err is Errno
 | ||||
| 		return 0, io.EOF | ||||
| 	} else { | ||||
| 		return n, err | ||||
|  | @ -254,7 +256,7 @@ func (f *win32File) Read(b []byte) (int, error) { | |||
| 
 | ||||
| // Write writes to a file handle.
 | ||||
| func (f *win32File) Write(b []byte) (int, error) { | ||||
| 	c, err := f.prepareIo() | ||||
| 	c, err := f.prepareIO() | ||||
| 	if err != nil { | ||||
| 		return 0, err | ||||
| 	} | ||||
|  | @ -266,7 +268,7 @@ func (f *win32File) Write(b []byte) (int, error) { | |||
| 
 | ||||
| 	var bytes uint32 | ||||
| 	err = syscall.WriteFile(f.handle, b, &bytes, &c.o) | ||||
| 	n, err := f.asyncIo(c, &f.writeDeadline, bytes, err) | ||||
| 	n, err := f.asyncIO(c, &f.writeDeadline, bytes, err) | ||||
| 	runtime.KeepAlive(b) | ||||
| 	return n, err | ||||
| } | ||||
|  |  | |||
|  | @ -1,3 +1,4 @@ | |||
| //go:build windows
 | ||||
| // +build windows
 | ||||
| 
 | ||||
| package winio | ||||
|  | @ -14,13 +15,18 @@ import ( | |||
| type FileBasicInfo struct { | ||||
| 	CreationTime, LastAccessTime, LastWriteTime, ChangeTime windows.Filetime | ||||
| 	FileAttributes                                          uint32 | ||||
| 	pad                                                     uint32 // padding
 | ||||
| 	_                                                       uint32 // padding
 | ||||
| } | ||||
| 
 | ||||
| // GetFileBasicInfo retrieves times and attributes for a file.
 | ||||
| func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) { | ||||
| 	bi := &FileBasicInfo{} | ||||
| 	if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil { | ||||
| 	if err := windows.GetFileInformationByHandleEx( | ||||
| 		windows.Handle(f.Fd()), | ||||
| 		windows.FileBasicInfo, | ||||
| 		(*byte)(unsafe.Pointer(bi)), | ||||
| 		uint32(unsafe.Sizeof(*bi)), | ||||
| 	); err != nil { | ||||
| 		return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} | ||||
| 	} | ||||
| 	runtime.KeepAlive(f) | ||||
|  | @ -29,7 +35,12 @@ func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) { | |||
| 
 | ||||
| // SetFileBasicInfo sets times and attributes for a file.
 | ||||
| func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error { | ||||
| 	if err := windows.SetFileInformationByHandle(windows.Handle(f.Fd()), windows.FileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil { | ||||
| 	if err := windows.SetFileInformationByHandle( | ||||
| 		windows.Handle(f.Fd()), | ||||
| 		windows.FileBasicInfo, | ||||
| 		(*byte)(unsafe.Pointer(bi)), | ||||
| 		uint32(unsafe.Sizeof(*bi)), | ||||
| 	); err != nil { | ||||
| 		return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err} | ||||
| 	} | ||||
| 	runtime.KeepAlive(f) | ||||
|  | @ -48,7 +59,10 @@ type FileStandardInfo struct { | |||
| // GetFileStandardInfo retrieves ended information for the file.
 | ||||
| func GetFileStandardInfo(f *os.File) (*FileStandardInfo, error) { | ||||
| 	si := &FileStandardInfo{} | ||||
| 	if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileStandardInfo, (*byte)(unsafe.Pointer(si)), uint32(unsafe.Sizeof(*si))); err != nil { | ||||
| 	if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), | ||||
| 		windows.FileStandardInfo, | ||||
| 		(*byte)(unsafe.Pointer(si)), | ||||
| 		uint32(unsafe.Sizeof(*si))); err != nil { | ||||
| 		return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} | ||||
| 	} | ||||
| 	runtime.KeepAlive(f) | ||||
|  | @ -65,7 +79,12 @@ type FileIDInfo struct { | |||
| // GetFileID retrieves the unique (volume, file ID) pair for a file.
 | ||||
| func GetFileID(f *os.File) (*FileIDInfo, error) { | ||||
| 	fileID := &FileIDInfo{} | ||||
| 	if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileIdInfo, (*byte)(unsafe.Pointer(fileID)), uint32(unsafe.Sizeof(*fileID))); err != nil { | ||||
| 	if err := windows.GetFileInformationByHandleEx( | ||||
| 		windows.Handle(f.Fd()), | ||||
| 		windows.FileIdInfo, | ||||
| 		(*byte)(unsafe.Pointer(fileID)), | ||||
| 		uint32(unsafe.Sizeof(*fileID)), | ||||
| 	); err != nil { | ||||
| 		return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} | ||||
| 	} | ||||
| 	runtime.KeepAlive(f) | ||||
|  |  | |||
|  | @ -4,6 +4,8 @@ | |||
| package winio | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"net" | ||||
|  | @ -12,16 +14,87 @@ import ( | |||
| 	"time" | ||||
| 	"unsafe" | ||||
| 
 | ||||
| 	"golang.org/x/sys/windows" | ||||
| 
 | ||||
| 	"github.com/Microsoft/go-winio/internal/socket" | ||||
| 	"github.com/Microsoft/go-winio/pkg/guid" | ||||
| ) | ||||
| 
 | ||||
| //sys bind(s syscall.Handle, name unsafe.Pointer, namelen int32) (err error) [failretval==socketError] = ws2_32.bind
 | ||||
| const afHVSock = 34 // AF_HYPERV
 | ||||
| 
 | ||||
| const ( | ||||
| 	afHvSock = 34 // AF_HYPERV
 | ||||
| // Well known Service and VM IDs
 | ||||
| //https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/user-guide/make-integration-service#vmid-wildcards
 | ||||
| 
 | ||||
| 	socketError = ^uintptr(0) | ||||
| ) | ||||
| // HvsockGUIDWildcard is the wildcard VmId for accepting connections from all partitions.
 | ||||
| func HvsockGUIDWildcard() guid.GUID { // 00000000-0000-0000-0000-000000000000
 | ||||
| 	return guid.GUID{} | ||||
| } | ||||
| 
 | ||||
| // HvsockGUIDBroadcast is the wildcard VmId for broadcasting sends to all partitions.
 | ||||
| func HvsockGUIDBroadcast() guid.GUID { //ffffffff-ffff-ffff-ffff-ffffffffffff
 | ||||
| 	return guid.GUID{ | ||||
| 		Data1: 0xffffffff, | ||||
| 		Data2: 0xffff, | ||||
| 		Data3: 0xffff, | ||||
| 		Data4: [8]uint8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // HvsockGUIDLoopback is the Loopback VmId for accepting connections to the same partition as the connector.
 | ||||
| func HvsockGUIDLoopback() guid.GUID { // e0e16197-dd56-4a10-9195-5ee7a155a838
 | ||||
| 	return guid.GUID{ | ||||
| 		Data1: 0xe0e16197, | ||||
| 		Data2: 0xdd56, | ||||
| 		Data3: 0x4a10, | ||||
| 		Data4: [8]uint8{0x91, 0x95, 0x5e, 0xe7, 0xa1, 0x55, 0xa8, 0x38}, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // HvsockGUIDSiloHost is the address of a silo's host partition:
 | ||||
| //   - The silo host of a hosted silo is the utility VM.
 | ||||
| //   - The silo host of a silo on a physical host is the physical host.
 | ||||
| func HvsockGUIDSiloHost() guid.GUID { // 36bd0c5c-7276-4223-88ba-7d03b654c568
 | ||||
| 	return guid.GUID{ | ||||
| 		Data1: 0x36bd0c5c, | ||||
| 		Data2: 0x7276, | ||||
| 		Data3: 0x4223, | ||||
| 		Data4: [8]byte{0x88, 0xba, 0x7d, 0x03, 0xb6, 0x54, 0xc5, 0x68}, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // HvsockGUIDChildren is the wildcard VmId for accepting connections from the connector's child partitions.
 | ||||
| func HvsockGUIDChildren() guid.GUID { // 90db8b89-0d35-4f79-8ce9-49ea0ac8b7cd
 | ||||
| 	return guid.GUID{ | ||||
| 		Data1: 0x90db8b89, | ||||
| 		Data2: 0xd35, | ||||
| 		Data3: 0x4f79, | ||||
| 		Data4: [8]uint8{0x8c, 0xe9, 0x49, 0xea, 0xa, 0xc8, 0xb7, 0xcd}, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // HvsockGUIDParent is the wildcard VmId for accepting connections from the connector's parent partition.
 | ||||
| // Listening on this VmId accepts connection from:
 | ||||
| //   - Inside silos: silo host partition.
 | ||||
| //   - Inside hosted silo: host of the VM.
 | ||||
| //   - Inside VM: VM host.
 | ||||
| //   - Physical host: Not supported.
 | ||||
| func HvsockGUIDParent() guid.GUID { // a42e7cda-d03f-480c-9cc2-a4de20abb878
 | ||||
| 	return guid.GUID{ | ||||
| 		Data1: 0xa42e7cda, | ||||
| 		Data2: 0xd03f, | ||||
| 		Data3: 0x480c, | ||||
| 		Data4: [8]uint8{0x9c, 0xc2, 0xa4, 0xde, 0x20, 0xab, 0xb8, 0x78}, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // hvsockVsockServiceTemplate is the Service GUID used for the VSOCK protocol.
 | ||||
| func hvsockVsockServiceTemplate() guid.GUID { // 00000000-facb-11e6-bd58-64006a7986d3
 | ||||
| 	return guid.GUID{ | ||||
| 		Data2: 0xfacb, | ||||
| 		Data3: 0x11e6, | ||||
| 		Data4: [8]uint8{0xbd, 0x58, 0x64, 0x00, 0x6a, 0x79, 0x86, 0xd3}, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // An HvsockAddr is an address for a AF_HYPERV socket.
 | ||||
| type HvsockAddr struct { | ||||
|  | @ -36,8 +109,10 @@ type rawHvsockAddr struct { | |||
| 	ServiceID guid.GUID | ||||
| } | ||||
| 
 | ||||
| var _ socket.RawSockaddr = &rawHvsockAddr{} | ||||
| 
 | ||||
| // Network returns the address's network name, "hvsock".
 | ||||
| func (addr *HvsockAddr) Network() string { | ||||
| func (*HvsockAddr) Network() string { | ||||
| 	return "hvsock" | ||||
| } | ||||
| 
 | ||||
|  | @ -47,14 +122,14 @@ func (addr *HvsockAddr) String() string { | |||
| 
 | ||||
| // VsockServiceID returns an hvsock service ID corresponding to the specified AF_VSOCK port.
 | ||||
| func VsockServiceID(port uint32) guid.GUID { | ||||
| 	g, _ := guid.FromString("00000000-facb-11e6-bd58-64006a7986d3") | ||||
| 	g := hvsockVsockServiceTemplate() // make a copy
 | ||||
| 	g.Data1 = port | ||||
| 	return g | ||||
| } | ||||
| 
 | ||||
| func (addr *HvsockAddr) raw() rawHvsockAddr { | ||||
| 	return rawHvsockAddr{ | ||||
| 		Family:    afHvSock, | ||||
| 		Family:    afHVSock, | ||||
| 		VMID:      addr.VMID, | ||||
| 		ServiceID: addr.ServiceID, | ||||
| 	} | ||||
|  | @ -65,20 +140,48 @@ func (addr *HvsockAddr) fromRaw(raw *rawHvsockAddr) { | |||
| 	addr.ServiceID = raw.ServiceID | ||||
| } | ||||
| 
 | ||||
| // Sockaddr returns a pointer to and the size of this struct.
 | ||||
| //
 | ||||
| // Implements the [socket.RawSockaddr] interface, and allows use in
 | ||||
| // [socket.Bind] and [socket.ConnectEx].
 | ||||
| func (r *rawHvsockAddr) Sockaddr() (unsafe.Pointer, int32, error) { | ||||
| 	return unsafe.Pointer(r), int32(unsafe.Sizeof(rawHvsockAddr{})), nil | ||||
| } | ||||
| 
 | ||||
| // Sockaddr interface allows use with `sockets.Bind()` and `.ConnectEx()`.
 | ||||
| func (r *rawHvsockAddr) FromBytes(b []byte) error { | ||||
| 	n := int(unsafe.Sizeof(rawHvsockAddr{})) | ||||
| 
 | ||||
| 	if len(b) < n { | ||||
| 		return fmt.Errorf("got %d, want %d: %w", len(b), n, socket.ErrBufferSize) | ||||
| 	} | ||||
| 
 | ||||
| 	copy(unsafe.Slice((*byte)(unsafe.Pointer(r)), n), b[:n]) | ||||
| 	if r.Family != afHVSock { | ||||
| 		return fmt.Errorf("got %d, want %d: %w", r.Family, afHVSock, socket.ErrAddrFamily) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // HvsockListener is a socket listener for the AF_HYPERV address family.
 | ||||
| type HvsockListener struct { | ||||
| 	sock *win32File | ||||
| 	addr HvsockAddr | ||||
| } | ||||
| 
 | ||||
| var _ net.Listener = &HvsockListener{} | ||||
| 
 | ||||
| // HvsockConn is a connected socket of the AF_HYPERV address family.
 | ||||
| type HvsockConn struct { | ||||
| 	sock          *win32File | ||||
| 	local, remote HvsockAddr | ||||
| } | ||||
| 
 | ||||
| func newHvSocket() (*win32File, error) { | ||||
| 	fd, err := syscall.Socket(afHvSock, syscall.SOCK_STREAM, 1) | ||||
| var _ net.Conn = &HvsockConn{} | ||||
| 
 | ||||
| func newHVSocket() (*win32File, error) { | ||||
| 	fd, err := syscall.Socket(afHVSock, syscall.SOCK_STREAM, 1) | ||||
| 	if err != nil { | ||||
| 		return nil, os.NewSyscallError("socket", err) | ||||
| 	} | ||||
|  | @ -94,12 +197,12 @@ func newHvSocket() (*win32File, error) { | |||
| // ListenHvsock listens for connections on the specified hvsock address.
 | ||||
| func ListenHvsock(addr *HvsockAddr) (_ *HvsockListener, err error) { | ||||
| 	l := &HvsockListener{addr: *addr} | ||||
| 	sock, err := newHvSocket() | ||||
| 	sock, err := newHVSocket() | ||||
| 	if err != nil { | ||||
| 		return nil, l.opErr("listen", err) | ||||
| 	} | ||||
| 	sa := addr.raw() | ||||
| 	err = bind(sock.handle, unsafe.Pointer(&sa), int32(unsafe.Sizeof(sa))) | ||||
| 	err = socket.Bind(windows.Handle(sock.handle), &sa) | ||||
| 	if err != nil { | ||||
| 		return nil, l.opErr("listen", os.NewSyscallError("socket", err)) | ||||
| 	} | ||||
|  | @ -121,7 +224,7 @@ func (l *HvsockListener) Addr() net.Addr { | |||
| 
 | ||||
| // Accept waits for the next connection and returns it.
 | ||||
| func (l *HvsockListener) Accept() (_ net.Conn, err error) { | ||||
| 	sock, err := newHvSocket() | ||||
| 	sock, err := newHVSocket() | ||||
| 	if err != nil { | ||||
| 		return nil, l.opErr("accept", err) | ||||
| 	} | ||||
|  | @ -130,27 +233,42 @@ func (l *HvsockListener) Accept() (_ net.Conn, err error) { | |||
| 			sock.Close() | ||||
| 		} | ||||
| 	}() | ||||
| 	c, err := l.sock.prepareIo() | ||||
| 	c, err := l.sock.prepareIO() | ||||
| 	if err != nil { | ||||
| 		return nil, l.opErr("accept", err) | ||||
| 	} | ||||
| 	defer l.sock.wg.Done() | ||||
| 
 | ||||
| 	// AcceptEx, per documentation, requires an extra 16 bytes per address.
 | ||||
| 	//
 | ||||
| 	// https://docs.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-acceptex
 | ||||
| 	const addrlen = uint32(16 + unsafe.Sizeof(rawHvsockAddr{})) | ||||
| 	var addrbuf [addrlen * 2]byte | ||||
| 
 | ||||
| 	var bytes uint32 | ||||
| 	err = syscall.AcceptEx(l.sock.handle, sock.handle, &addrbuf[0], 0, addrlen, addrlen, &bytes, &c.o) | ||||
| 	_, err = l.sock.asyncIo(c, nil, bytes, err) | ||||
| 	if err != nil { | ||||
| 	err = syscall.AcceptEx(l.sock.handle, sock.handle, &addrbuf[0], 0 /*rxdatalen*/, addrlen, addrlen, &bytes, &c.o) | ||||
| 	if _, err = l.sock.asyncIO(c, nil, bytes, err); err != nil { | ||||
| 		return nil, l.opErr("accept", os.NewSyscallError("acceptex", err)) | ||||
| 	} | ||||
| 
 | ||||
| 	conn := &HvsockConn{ | ||||
| 		sock: sock, | ||||
| 	} | ||||
| 	// The local address returned in the AcceptEx buffer is the same as the Listener socket's
 | ||||
| 	// address. However, the service GUID reported by GetSockName is different from the Listeners
 | ||||
| 	// socket, and is sometimes the same as the local address of the socket that dialed the
 | ||||
| 	// address, with the service GUID.Data1 incremented, but othertimes is different.
 | ||||
| 	// todo: does the local address matter? is the listener's address or the actual address appropriate?
 | ||||
| 	conn.local.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[0]))) | ||||
| 	conn.remote.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[addrlen]))) | ||||
| 
 | ||||
| 	// initialize the accepted socket and update its properties with those of the listening socket
 | ||||
| 	if err = windows.Setsockopt(windows.Handle(sock.handle), | ||||
| 		windows.SOL_SOCKET, windows.SO_UPDATE_ACCEPT_CONTEXT, | ||||
| 		(*byte)(unsafe.Pointer(&l.sock.handle)), int32(unsafe.Sizeof(l.sock.handle))); err != nil { | ||||
| 		return nil, conn.opErr("accept", os.NewSyscallError("setsockopt", err)) | ||||
| 	} | ||||
| 
 | ||||
| 	sock = nil | ||||
| 	return conn, nil | ||||
| } | ||||
|  | @ -160,43 +278,171 @@ func (l *HvsockListener) Close() error { | |||
| 	return l.sock.Close() | ||||
| } | ||||
| 
 | ||||
| /* Need to finish ConnectEx handling | ||||
| func DialHvsock(ctx context.Context, addr *HvsockAddr) (*HvsockConn, error) { | ||||
| 	sock, err := newHvSocket() | ||||
| // HvsockDialer configures and dials a Hyper-V Socket (ie, [HvsockConn]).
 | ||||
| type HvsockDialer struct { | ||||
| 	// Deadline is the time the Dial operation must connect before erroring.
 | ||||
| 	Deadline time.Time | ||||
| 
 | ||||
| 	// Retries is the number of additional connects to try if the connection times out, is refused,
 | ||||
| 	// or the host is unreachable
 | ||||
| 	Retries uint | ||||
| 
 | ||||
| 	// RetryWait is the time to wait after a connection error to retry
 | ||||
| 	RetryWait time.Duration | ||||
| 
 | ||||
| 	rt *time.Timer // redial wait timer
 | ||||
| } | ||||
| 
 | ||||
| // Dial the Hyper-V socket at addr.
 | ||||
| //
 | ||||
| // See [HvsockDialer.Dial] for more information.
 | ||||
| func Dial(ctx context.Context, addr *HvsockAddr) (conn *HvsockConn, err error) { | ||||
| 	return (&HvsockDialer{}).Dial(ctx, addr) | ||||
| } | ||||
| 
 | ||||
| // Dial attempts to connect to the Hyper-V socket at addr, and returns a connection if successful.
 | ||||
| // Will attempt (HvsockDialer).Retries if dialing fails, waiting (HvsockDialer).RetryWait between
 | ||||
| // retries.
 | ||||
| //
 | ||||
| // Dialing can be cancelled either by providing (HvsockDialer).Deadline, or cancelling ctx.
 | ||||
| func (d *HvsockDialer) Dial(ctx context.Context, addr *HvsockAddr) (conn *HvsockConn, err error) { | ||||
| 	op := "dial" | ||||
| 	// create the conn early to use opErr()
 | ||||
| 	conn = &HvsockConn{ | ||||
| 		remote: *addr, | ||||
| 	} | ||||
| 
 | ||||
| 	if !d.Deadline.IsZero() { | ||||
| 		var cancel context.CancelFunc | ||||
| 		ctx, cancel = context.WithDeadline(ctx, d.Deadline) | ||||
| 		defer cancel() | ||||
| 	} | ||||
| 
 | ||||
| 	// preemptive timeout/cancellation check
 | ||||
| 	if err = ctx.Err(); err != nil { | ||||
| 		return nil, conn.opErr(op, err) | ||||
| 	} | ||||
| 
 | ||||
| 	sock, err := newHVSocket() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 		return nil, conn.opErr(op, err) | ||||
| 	} | ||||
| 	defer func() { | ||||
| 		if sock != nil { | ||||
| 			sock.Close() | ||||
| 		} | ||||
| 	}() | ||||
| 	c, err := sock.prepareIo() | ||||
| 
 | ||||
| 	sa := addr.raw() | ||||
| 	err = socket.Bind(windows.Handle(sock.handle), &sa) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 		return nil, conn.opErr(op, os.NewSyscallError("bind", err)) | ||||
| 	} | ||||
| 
 | ||||
| 	c, err := sock.prepareIO() | ||||
| 	if err != nil { | ||||
| 		return nil, conn.opErr(op, err) | ||||
| 	} | ||||
| 	defer sock.wg.Done() | ||||
| 	var bytes uint32 | ||||
| 	err = windows.ConnectEx(windows.Handle(sock.handle), sa, nil, 0, &bytes, &c.o) | ||||
| 	_, err = sock.asyncIo(ctx, c, nil, bytes, err) | ||||
| 	for i := uint(0); i <= d.Retries; i++ { | ||||
| 		err = socket.ConnectEx( | ||||
| 			windows.Handle(sock.handle), | ||||
| 			&sa, | ||||
| 			nil, // sendBuf
 | ||||
| 			0,   // sendDataLen
 | ||||
| 			&bytes, | ||||
| 			(*windows.Overlapped)(unsafe.Pointer(&c.o))) | ||||
| 		_, err = sock.asyncIO(c, nil, bytes, err) | ||||
| 		if i < d.Retries && canRedial(err) { | ||||
| 			if err = d.redialWait(ctx); err == nil { | ||||
| 				continue | ||||
| 			} | ||||
| 		} | ||||
| 		break | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 		return nil, conn.opErr(op, os.NewSyscallError("connectex", err)) | ||||
| 	} | ||||
| 	conn := &HvsockConn{ | ||||
| 		sock:   sock, | ||||
| 		remote: *addr, | ||||
| 
 | ||||
| 	// update the connection properties, so shutdown can be used
 | ||||
| 	if err = windows.Setsockopt( | ||||
| 		windows.Handle(sock.handle), | ||||
| 		windows.SOL_SOCKET, | ||||
| 		windows.SO_UPDATE_CONNECT_CONTEXT, | ||||
| 		nil, // optvalue
 | ||||
| 		0,   // optlen
 | ||||
| 	); err != nil { | ||||
| 		return nil, conn.opErr(op, os.NewSyscallError("setsockopt", err)) | ||||
| 	} | ||||
| 
 | ||||
| 	// get the local name
 | ||||
| 	var sal rawHvsockAddr | ||||
| 	err = socket.GetSockName(windows.Handle(sock.handle), &sal) | ||||
| 	if err != nil { | ||||
| 		return nil, conn.opErr(op, os.NewSyscallError("getsockname", err)) | ||||
| 	} | ||||
| 	conn.local.fromRaw(&sal) | ||||
| 
 | ||||
| 	// one last check for timeout, since asyncIO doesn't check the context
 | ||||
| 	if err = ctx.Err(); err != nil { | ||||
| 		return nil, conn.opErr(op, err) | ||||
| 	} | ||||
| 
 | ||||
| 	conn.sock = sock | ||||
| 	sock = nil | ||||
| 
 | ||||
| 	return conn, nil | ||||
| } | ||||
| */ | ||||
| 
 | ||||
| // redialWait waits before attempting to redial, resetting the timer as appropriate.
 | ||||
| func (d *HvsockDialer) redialWait(ctx context.Context) (err error) { | ||||
| 	if d.RetryWait == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	if d.rt == nil { | ||||
| 		d.rt = time.NewTimer(d.RetryWait) | ||||
| 	} else { | ||||
| 		// should already be stopped and drained
 | ||||
| 		d.rt.Reset(d.RetryWait) | ||||
| 	} | ||||
| 
 | ||||
| 	select { | ||||
| 	case <-ctx.Done(): | ||||
| 	case <-d.rt.C: | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	// stop and drain the timer
 | ||||
| 	if !d.rt.Stop() { | ||||
| 		<-d.rt.C | ||||
| 	} | ||||
| 	return ctx.Err() | ||||
| } | ||||
| 
 | ||||
| // assumes error is a plain, unwrapped syscall.Errno provided by direct syscall.
 | ||||
| func canRedial(err error) bool { | ||||
| 	//nolint:errorlint // guaranteed to be an Errno
 | ||||
| 	switch err { | ||||
| 	case windows.WSAECONNREFUSED, windows.WSAENETUNREACH, windows.WSAETIMEDOUT, | ||||
| 		windows.ERROR_CONNECTION_REFUSED, windows.ERROR_CONNECTION_UNAVAIL: | ||||
| 		return true | ||||
| 	default: | ||||
| 		return false | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (conn *HvsockConn) opErr(op string, err error) error { | ||||
| 	// translate from "file closed" to "socket closed"
 | ||||
| 	if errors.Is(err, ErrFileClosed) { | ||||
| 		err = socket.ErrSocketClosed | ||||
| 	} | ||||
| 	return &net.OpError{Op: op, Net: "hvsock", Source: &conn.local, Addr: &conn.remote, Err: err} | ||||
| } | ||||
| 
 | ||||
| func (conn *HvsockConn) Read(b []byte) (int, error) { | ||||
| 	c, err := conn.sock.prepareIo() | ||||
| 	c, err := conn.sock.prepareIO() | ||||
| 	if err != nil { | ||||
| 		return 0, conn.opErr("read", err) | ||||
| 	} | ||||
|  | @ -204,10 +450,11 @@ func (conn *HvsockConn) Read(b []byte) (int, error) { | |||
| 	buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))} | ||||
| 	var flags, bytes uint32 | ||||
| 	err = syscall.WSARecv(conn.sock.handle, &buf, 1, &bytes, &flags, &c.o, nil) | ||||
| 	n, err := conn.sock.asyncIo(c, &conn.sock.readDeadline, bytes, err) | ||||
| 	n, err := conn.sock.asyncIO(c, &conn.sock.readDeadline, bytes, err) | ||||
| 	if err != nil { | ||||
| 		if _, ok := err.(syscall.Errno); ok { | ||||
| 			err = os.NewSyscallError("wsarecv", err) | ||||
| 		var eno windows.Errno | ||||
| 		if errors.As(err, &eno) { | ||||
| 			err = os.NewSyscallError("wsarecv", eno) | ||||
| 		} | ||||
| 		return 0, conn.opErr("read", err) | ||||
| 	} else if n == 0 { | ||||
|  | @ -230,7 +477,7 @@ func (conn *HvsockConn) Write(b []byte) (int, error) { | |||
| } | ||||
| 
 | ||||
| func (conn *HvsockConn) write(b []byte) (int, error) { | ||||
| 	c, err := conn.sock.prepareIo() | ||||
| 	c, err := conn.sock.prepareIO() | ||||
| 	if err != nil { | ||||
| 		return 0, conn.opErr("write", err) | ||||
| 	} | ||||
|  | @ -238,10 +485,11 @@ func (conn *HvsockConn) write(b []byte) (int, error) { | |||
| 	buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))} | ||||
| 	var bytes uint32 | ||||
| 	err = syscall.WSASend(conn.sock.handle, &buf, 1, &bytes, 0, &c.o, nil) | ||||
| 	n, err := conn.sock.asyncIo(c, &conn.sock.writeDeadline, bytes, err) | ||||
| 	n, err := conn.sock.asyncIO(c, &conn.sock.writeDeadline, bytes, err) | ||||
| 	if err != nil { | ||||
| 		if _, ok := err.(syscall.Errno); ok { | ||||
| 			err = os.NewSyscallError("wsasend", err) | ||||
| 		var eno windows.Errno | ||||
| 		if errors.As(err, &eno) { | ||||
| 			err = os.NewSyscallError("wsasend", eno) | ||||
| 		} | ||||
| 		return 0, conn.opErr("write", err) | ||||
| 	} | ||||
|  | @ -257,13 +505,19 @@ func (conn *HvsockConn) IsClosed() bool { | |||
| 	return conn.sock.IsClosed() | ||||
| } | ||||
| 
 | ||||
| // shutdown disables sending or receiving on a socket.
 | ||||
| func (conn *HvsockConn) shutdown(how int) error { | ||||
| 	if conn.IsClosed() { | ||||
| 		return ErrFileClosed | ||||
| 		return socket.ErrSocketClosed | ||||
| 	} | ||||
| 
 | ||||
| 	err := syscall.Shutdown(conn.sock.handle, how) | ||||
| 	if err != nil { | ||||
| 		// If the connection was closed, shutdowns fail with "not connected"
 | ||||
| 		if errors.Is(err, windows.WSAENOTCONN) || | ||||
| 			errors.Is(err, windows.WSAESHUTDOWN) { | ||||
| 			err = socket.ErrSocketClosed | ||||
| 		} | ||||
| 		return os.NewSyscallError("shutdown", err) | ||||
| 	} | ||||
| 	return nil | ||||
|  | @ -273,7 +527,7 @@ func (conn *HvsockConn) shutdown(how int) error { | |||
| func (conn *HvsockConn) CloseRead() error { | ||||
| 	err := conn.shutdown(syscall.SHUT_RD) | ||||
| 	if err != nil { | ||||
| 		return conn.opErr("close", err) | ||||
| 		return conn.opErr("closeread", err) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | @ -283,7 +537,7 @@ func (conn *HvsockConn) CloseRead() error { | |||
| func (conn *HvsockConn) CloseWrite() error { | ||||
| 	err := conn.shutdown(syscall.SHUT_WR) | ||||
| 	if err != nil { | ||||
| 		return conn.opErr("close", err) | ||||
| 		return conn.opErr("closewrite", err) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | @ -300,8 +554,13 @@ func (conn *HvsockConn) RemoteAddr() net.Addr { | |||
| 
 | ||||
| // SetDeadline implements the net.Conn SetDeadline method.
 | ||||
| func (conn *HvsockConn) SetDeadline(t time.Time) error { | ||||
| 	conn.SetReadDeadline(t) | ||||
| 	conn.SetWriteDeadline(t) | ||||
| 	// todo: implement `SetDeadline` for `win32File`
 | ||||
| 	if err := conn.SetReadDeadline(t); err != nil { | ||||
| 		return fmt.Errorf("set read deadline: %w", err) | ||||
| 	} | ||||
| 	if err := conn.SetWriteDeadline(t); err != nil { | ||||
| 		return fmt.Errorf("set write deadline: %w", err) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,20 @@ | |||
| package socket | ||||
| 
 | ||||
| import ( | ||||
| 	"unsafe" | ||||
| ) | ||||
| 
 | ||||
| // RawSockaddr allows structs to be used with [Bind] and [ConnectEx]. The
 | ||||
| // struct must meet the Win32 sockaddr requirements specified here:
 | ||||
| // https://docs.microsoft.com/en-us/windows/win32/winsock/sockaddr-2
 | ||||
| //
 | ||||
| // Specifically, the struct size must be least larger than an int16 (unsigned short)
 | ||||
| // for the address family.
 | ||||
| type RawSockaddr interface { | ||||
| 	// Sockaddr returns a pointer to the RawSockaddr and its struct size, allowing
 | ||||
| 	// for the RawSockaddr's data to be overwritten by syscalls (if necessary).
 | ||||
| 	//
 | ||||
| 	// It is the callers responsibility to validate that the values are valid; invalid
 | ||||
| 	// pointers or size can cause a panic.
 | ||||
| 	Sockaddr() (unsafe.Pointer, int32, error) | ||||
| } | ||||
|  | @ -0,0 +1,179 @@ | |||
| //go:build windows
 | ||||
| 
 | ||||
| package socket | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"net" | ||||
| 	"sync" | ||||
| 	"syscall" | ||||
| 	"unsafe" | ||||
| 
 | ||||
| 	"github.com/Microsoft/go-winio/pkg/guid" | ||||
| 	"golang.org/x/sys/windows" | ||||
| ) | ||||
| 
 | ||||
| //go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go socket.go
 | ||||
| 
 | ||||
| //sys getsockname(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) [failretval==socketError] = ws2_32.getsockname
 | ||||
| //sys getpeername(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) [failretval==socketError] = ws2_32.getpeername
 | ||||
| //sys bind(s windows.Handle, name unsafe.Pointer, namelen int32) (err error) [failretval==socketError] = ws2_32.bind
 | ||||
| 
 | ||||
| const socketError = uintptr(^uint32(0)) | ||||
| 
 | ||||
| var ( | ||||
| 	// todo(helsaawy): create custom error types to store the desired vs actual size and addr family?
 | ||||
| 
 | ||||
| 	ErrBufferSize     = errors.New("buffer size") | ||||
| 	ErrAddrFamily     = errors.New("address family") | ||||
| 	ErrInvalidPointer = errors.New("invalid pointer") | ||||
| 	ErrSocketClosed   = fmt.Errorf("socket closed: %w", net.ErrClosed) | ||||
| ) | ||||
| 
 | ||||
| // todo(helsaawy): replace these with generics, ie: GetSockName[S RawSockaddr](s windows.Handle) (S, error)
 | ||||
| 
 | ||||
| // GetSockName writes the local address of socket s to the [RawSockaddr] rsa.
 | ||||
| // If rsa is not large enough, the [windows.WSAEFAULT] is returned.
 | ||||
| func GetSockName(s windows.Handle, rsa RawSockaddr) error { | ||||
| 	ptr, l, err := rsa.Sockaddr() | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("could not retrieve socket pointer and size: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	// although getsockname returns WSAEFAULT if the buffer is too small, it does not set
 | ||||
| 	// &l to the correct size, so--apart from doubling the buffer repeatedly--there is no remedy
 | ||||
| 	return getsockname(s, ptr, &l) | ||||
| } | ||||
| 
 | ||||
| // GetPeerName returns the remote address the socket is connected to.
 | ||||
| //
 | ||||
| // See [GetSockName] for more information.
 | ||||
| func GetPeerName(s windows.Handle, rsa RawSockaddr) error { | ||||
| 	ptr, l, err := rsa.Sockaddr() | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("could not retrieve socket pointer and size: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	return getpeername(s, ptr, &l) | ||||
| } | ||||
| 
 | ||||
| func Bind(s windows.Handle, rsa RawSockaddr) (err error) { | ||||
| 	ptr, l, err := rsa.Sockaddr() | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("could not retrieve socket pointer and size: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	return bind(s, ptr, l) | ||||
| } | ||||
| 
 | ||||
| // "golang.org/x/sys/windows".ConnectEx and .Bind only accept internal implementations of the
 | ||||
| // their sockaddr interface, so they cannot be used with HvsockAddr
 | ||||
| // Replicate functionality here from
 | ||||
| // https://cs.opensource.google/go/x/sys/+/master:windows/syscall_windows.go
 | ||||
| 
 | ||||
| // The function pointers to `AcceptEx`, `ConnectEx` and `GetAcceptExSockaddrs` must be loaded at
 | ||||
| // runtime via a WSAIoctl call:
 | ||||
| // https://docs.microsoft.com/en-us/windows/win32/api/Mswsock/nc-mswsock-lpfn_connectex#remarks
 | ||||
| 
 | ||||
| type runtimeFunc struct { | ||||
| 	id   guid.GUID | ||||
| 	once sync.Once | ||||
| 	addr uintptr | ||||
| 	err  error | ||||
| } | ||||
| 
 | ||||
| func (f *runtimeFunc) Load() error { | ||||
| 	f.once.Do(func() { | ||||
| 		var s windows.Handle | ||||
| 		s, f.err = windows.Socket(windows.AF_INET, windows.SOCK_STREAM, windows.IPPROTO_TCP) | ||||
| 		if f.err != nil { | ||||
| 			return | ||||
| 		} | ||||
| 		defer windows.CloseHandle(s) //nolint:errcheck
 | ||||
| 
 | ||||
| 		var n uint32 | ||||
| 		f.err = windows.WSAIoctl(s, | ||||
| 			windows.SIO_GET_EXTENSION_FUNCTION_POINTER, | ||||
| 			(*byte)(unsafe.Pointer(&f.id)), | ||||
| 			uint32(unsafe.Sizeof(f.id)), | ||||
| 			(*byte)(unsafe.Pointer(&f.addr)), | ||||
| 			uint32(unsafe.Sizeof(f.addr)), | ||||
| 			&n, | ||||
| 			nil, //overlapped
 | ||||
| 			0,   //completionRoutine
 | ||||
| 		) | ||||
| 	}) | ||||
| 	return f.err | ||||
| } | ||||
| 
 | ||||
| var ( | ||||
| 	// todo: add `AcceptEx` and `GetAcceptExSockaddrs`
 | ||||
| 	WSAID_CONNECTEX = guid.GUID{ //revive:disable-line:var-naming ALL_CAPS
 | ||||
| 		Data1: 0x25a207b9, | ||||
| 		Data2: 0xddf3, | ||||
| 		Data3: 0x4660, | ||||
| 		Data4: [8]byte{0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e}, | ||||
| 	} | ||||
| 
 | ||||
| 	connectExFunc = runtimeFunc{id: WSAID_CONNECTEX} | ||||
| ) | ||||
| 
 | ||||
| func ConnectEx( | ||||
| 	fd windows.Handle, | ||||
| 	rsa RawSockaddr, | ||||
| 	sendBuf *byte, | ||||
| 	sendDataLen uint32, | ||||
| 	bytesSent *uint32, | ||||
| 	overlapped *windows.Overlapped, | ||||
| ) error { | ||||
| 	if err := connectExFunc.Load(); err != nil { | ||||
| 		return fmt.Errorf("failed to load ConnectEx function pointer: %w", err) | ||||
| 	} | ||||
| 	ptr, n, err := rsa.Sockaddr() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return connectEx(fd, ptr, n, sendBuf, sendDataLen, bytesSent, overlapped) | ||||
| } | ||||
| 
 | ||||
| // BOOL LpfnConnectex(
 | ||||
| //   [in]           SOCKET s,
 | ||||
| //   [in]           const sockaddr *name,
 | ||||
| //   [in]           int namelen,
 | ||||
| //   [in, optional] PVOID lpSendBuffer,
 | ||||
| //   [in]           DWORD dwSendDataLength,
 | ||||
| //   [out]          LPDWORD lpdwBytesSent,
 | ||||
| //   [in]           LPOVERLAPPED lpOverlapped
 | ||||
| // )
 | ||||
| 
 | ||||
| func connectEx( | ||||
| 	s windows.Handle, | ||||
| 	name unsafe.Pointer, | ||||
| 	namelen int32, | ||||
| 	sendBuf *byte, | ||||
| 	sendDataLen uint32, | ||||
| 	bytesSent *uint32, | ||||
| 	overlapped *windows.Overlapped, | ||||
| ) (err error) { | ||||
| 	// todo: after upgrading to 1.18, switch from syscall.Syscall9 to syscall.SyscallN
 | ||||
| 	r1, _, e1 := syscall.Syscall9(connectExFunc.addr, | ||||
| 		7, | ||||
| 		uintptr(s), | ||||
| 		uintptr(name), | ||||
| 		uintptr(namelen), | ||||
| 		uintptr(unsafe.Pointer(sendBuf)), | ||||
| 		uintptr(sendDataLen), | ||||
| 		uintptr(unsafe.Pointer(bytesSent)), | ||||
| 		uintptr(unsafe.Pointer(overlapped)), | ||||
| 		0, | ||||
| 		0) | ||||
| 	if r1 == 0 { | ||||
| 		if e1 != 0 { | ||||
| 			err = error(e1) | ||||
| 		} else { | ||||
| 			err = syscall.EINVAL | ||||
| 		} | ||||
| 	} | ||||
| 	return err | ||||
| } | ||||
							
								
								
									
										72
									
								
								vendor/github.com/Microsoft/go-winio/internal/socket/zsyscall_windows.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										72
									
								
								vendor/github.com/Microsoft/go-winio/internal/socket/zsyscall_windows.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							|  | @ -0,0 +1,72 @@ | |||
| //go:build windows
 | ||||
| 
 | ||||
| // Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT.
 | ||||
| 
 | ||||
| package socket | ||||
| 
 | ||||
| import ( | ||||
| 	"syscall" | ||||
| 	"unsafe" | ||||
| 
 | ||||
| 	"golang.org/x/sys/windows" | ||||
| ) | ||||
| 
 | ||||
| var _ unsafe.Pointer | ||||
| 
 | ||||
| // Do the interface allocations only once for common
 | ||||
| // Errno values.
 | ||||
| const ( | ||||
| 	errnoERROR_IO_PENDING = 997 | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) | ||||
| 	errERROR_EINVAL     error = syscall.EINVAL | ||||
| ) | ||||
| 
 | ||||
| // errnoErr returns common boxed Errno values, to prevent
 | ||||
| // allocations at runtime.
 | ||||
| func errnoErr(e syscall.Errno) error { | ||||
| 	switch e { | ||||
| 	case 0: | ||||
| 		return errERROR_EINVAL | ||||
| 	case errnoERROR_IO_PENDING: | ||||
| 		return errERROR_IO_PENDING | ||||
| 	} | ||||
| 	// TODO: add more here, after collecting data on the common
 | ||||
| 	// error values see on Windows. (perhaps when running
 | ||||
| 	// all.bat?)
 | ||||
| 	return e | ||||
| } | ||||
| 
 | ||||
| var ( | ||||
| 	modws2_32 = windows.NewLazySystemDLL("ws2_32.dll") | ||||
| 
 | ||||
| 	procbind        = modws2_32.NewProc("bind") | ||||
| 	procgetpeername = modws2_32.NewProc("getpeername") | ||||
| 	procgetsockname = modws2_32.NewProc("getsockname") | ||||
| ) | ||||
| 
 | ||||
| func bind(s windows.Handle, name unsafe.Pointer, namelen int32) (err error) { | ||||
| 	r1, _, e1 := syscall.Syscall(procbind.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen)) | ||||
| 	if r1 == socketError { | ||||
| 		err = errnoErr(e1) | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func getpeername(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) { | ||||
| 	r1, _, e1 := syscall.Syscall(procgetpeername.Addr(), 3, uintptr(s), uintptr(name), uintptr(unsafe.Pointer(namelen))) | ||||
| 	if r1 == socketError { | ||||
| 		err = errnoErr(e1) | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func getsockname(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) { | ||||
| 	r1, _, e1 := syscall.Syscall(procgetsockname.Addr(), 3, uintptr(s), uintptr(name), uintptr(unsafe.Pointer(namelen))) | ||||
| 	if r1 == socketError { | ||||
| 		err = errnoErr(e1) | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | @ -1,3 +1,4 @@ | |||
| //go:build windows
 | ||||
| // +build windows
 | ||||
| 
 | ||||
| package winio | ||||
|  | @ -13,6 +14,8 @@ import ( | |||
| 	"syscall" | ||||
| 	"time" | ||||
| 	"unsafe" | ||||
| 
 | ||||
| 	"golang.org/x/sys/windows" | ||||
| ) | ||||
| 
 | ||||
| //sys connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) = ConnectNamedPipe
 | ||||
|  | @ -21,10 +24,10 @@ import ( | |||
| //sys getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo
 | ||||
| //sys getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW
 | ||||
| //sys localAlloc(uFlags uint32, length uint32) (ptr uintptr) = LocalAlloc
 | ||||
| //sys ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntstatus) = ntdll.NtCreateNamedPipeFile
 | ||||
| //sys rtlNtStatusToDosError(status ntstatus) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb
 | ||||
| //sys rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntstatus) = ntdll.RtlDosPathNameToNtPathName_U
 | ||||
| //sys rtlDefaultNpAcl(dacl *uintptr) (status ntstatus) = ntdll.RtlDefaultNpAcl
 | ||||
| //sys ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntStatus) = ntdll.NtCreateNamedPipeFile
 | ||||
| //sys rtlNtStatusToDosError(status ntStatus) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb
 | ||||
| //sys rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntStatus) = ntdll.RtlDosPathNameToNtPathName_U
 | ||||
| //sys rtlDefaultNpAcl(dacl *uintptr) (status ntStatus) = ntdll.RtlDefaultNpAcl
 | ||||
| 
 | ||||
| type ioStatusBlock struct { | ||||
| 	Status, Information uintptr | ||||
|  | @ -51,45 +54,22 @@ type securityDescriptor struct { | |||
| 	Control  uint16 | ||||
| 	Owner    uintptr | ||||
| 	Group    uintptr | ||||
| 	Sacl     uintptr | ||||
| 	Dacl     uintptr | ||||
| 	Sacl     uintptr //revive:disable-line:var-naming SACL, not Sacl
 | ||||
| 	Dacl     uintptr //revive:disable-line:var-naming DACL, not Dacl
 | ||||
| } | ||||
| 
 | ||||
| type ntstatus int32 | ||||
| type ntStatus int32 | ||||
| 
 | ||||
| func (status ntstatus) Err() error { | ||||
| func (status ntStatus) Err() error { | ||||
| 	if status >= 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return rtlNtStatusToDosError(status) | ||||
| } | ||||
| 
 | ||||
| const ( | ||||
| 	cERROR_PIPE_BUSY      = syscall.Errno(231) | ||||
| 	cERROR_NO_DATA        = syscall.Errno(232) | ||||
| 	cERROR_PIPE_CONNECTED = syscall.Errno(535) | ||||
| 	cERROR_SEM_TIMEOUT    = syscall.Errno(121) | ||||
| 
 | ||||
| 	cSECURITY_SQOS_PRESENT = 0x100000 | ||||
| 	cSECURITY_ANONYMOUS    = 0 | ||||
| 
 | ||||
| 	cPIPE_TYPE_MESSAGE = 4 | ||||
| 
 | ||||
| 	cPIPE_READMODE_MESSAGE = 2 | ||||
| 
 | ||||
| 	cFILE_OPEN   = 1 | ||||
| 	cFILE_CREATE = 2 | ||||
| 
 | ||||
| 	cFILE_PIPE_MESSAGE_TYPE          = 1 | ||||
| 	cFILE_PIPE_REJECT_REMOTE_CLIENTS = 2 | ||||
| 
 | ||||
| 	cSE_DACL_PRESENT = 4 | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	// ErrPipeListenerClosed is returned for pipe operations on listeners that have been closed.
 | ||||
| 	// This error should match net.errClosing since docker takes a dependency on its text.
 | ||||
| 	ErrPipeListenerClosed = errors.New("use of closed network connection") | ||||
| 	ErrPipeListenerClosed = net.ErrClosed | ||||
| 
 | ||||
| 	errPipeWriteClosed = errors.New("pipe has been closed for write") | ||||
| ) | ||||
|  | @ -116,9 +96,10 @@ func (f *win32Pipe) RemoteAddr() net.Addr { | |||
| } | ||||
| 
 | ||||
| func (f *win32Pipe) SetDeadline(t time.Time) error { | ||||
| 	f.SetReadDeadline(t) | ||||
| 	f.SetWriteDeadline(t) | ||||
| 	return nil | ||||
| 	if err := f.SetReadDeadline(t); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return f.SetWriteDeadline(t) | ||||
| } | ||||
| 
 | ||||
| // CloseWrite closes the write side of a message pipe in byte mode.
 | ||||
|  | @ -157,14 +138,14 @@ func (f *win32MessageBytePipe) Read(b []byte) (int, error) { | |||
| 		return 0, io.EOF | ||||
| 	} | ||||
| 	n, err := f.win32File.Read(b) | ||||
| 	if err == io.EOF { | ||||
| 	if err == io.EOF { //nolint:errorlint
 | ||||
| 		// If this was the result of a zero-byte read, then
 | ||||
| 		// it is possible that the read was due to a zero-size
 | ||||
| 		// message. Since we are simulating CloseWrite with a
 | ||||
| 		// zero-byte message, ensure that all future Read() calls
 | ||||
| 		// also return EOF.
 | ||||
| 		f.readEOF = true | ||||
| 	} else if err == syscall.ERROR_MORE_DATA { | ||||
| 	} else if err == syscall.ERROR_MORE_DATA { //nolint:errorlint // err is Errno
 | ||||
| 		// ERROR_MORE_DATA indicates that the pipe's read mode is message mode
 | ||||
| 		// and the message still has more bytes. Treat this as a success, since
 | ||||
| 		// this package presents all named pipes as byte streams.
 | ||||
|  | @ -173,7 +154,7 @@ func (f *win32MessageBytePipe) Read(b []byte) (int, error) { | |||
| 	return n, err | ||||
| } | ||||
| 
 | ||||
| func (s pipeAddress) Network() string { | ||||
| func (pipeAddress) Network() string { | ||||
| 	return "pipe" | ||||
| } | ||||
| 
 | ||||
|  | @ -184,16 +165,21 @@ func (s pipeAddress) String() string { | |||
| // tryDialPipe attempts to dial the pipe at `path` until `ctx` cancellation or timeout.
 | ||||
| func tryDialPipe(ctx context.Context, path *string, access uint32) (syscall.Handle, error) { | ||||
| 	for { | ||||
| 
 | ||||
| 		select { | ||||
| 		case <-ctx.Done(): | ||||
| 			return syscall.Handle(0), ctx.Err() | ||||
| 		default: | ||||
| 			h, err := createFile(*path, access, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED|cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0) | ||||
| 			h, err := createFile(*path, | ||||
| 				access, | ||||
| 				0, | ||||
| 				nil, | ||||
| 				syscall.OPEN_EXISTING, | ||||
| 				windows.FILE_FLAG_OVERLAPPED|windows.SECURITY_SQOS_PRESENT|windows.SECURITY_ANONYMOUS, | ||||
| 				0) | ||||
| 			if err == nil { | ||||
| 				return h, nil | ||||
| 			} | ||||
| 			if err != cERROR_PIPE_BUSY { | ||||
| 			if err != windows.ERROR_PIPE_BUSY { //nolint:errorlint // err is Errno
 | ||||
| 				return h, &os.PathError{Err: err, Op: "open", Path: *path} | ||||
| 			} | ||||
| 			// Wait 10 msec and try again. This is a rather simplistic
 | ||||
|  | @ -213,9 +199,10 @@ func DialPipe(path string, timeout *time.Duration) (net.Conn, error) { | |||
| 	} else { | ||||
| 		absTimeout = time.Now().Add(2 * time.Second) | ||||
| 	} | ||||
| 	ctx, _ := context.WithDeadline(context.Background(), absTimeout) | ||||
| 	ctx, cancel := context.WithDeadline(context.Background(), absTimeout) | ||||
| 	defer cancel() | ||||
| 	conn, err := DialPipeContext(ctx, path) | ||||
| 	if err == context.DeadlineExceeded { | ||||
| 	if errors.Is(err, context.DeadlineExceeded) { | ||||
| 		return nil, ErrTimeout | ||||
| 	} | ||||
| 	return conn, err | ||||
|  | @ -251,7 +238,7 @@ func DialPipeAccess(ctx context.Context, path string, access uint32) (net.Conn, | |||
| 
 | ||||
| 	// If the pipe is in message mode, return a message byte pipe, which
 | ||||
| 	// supports CloseWrite().
 | ||||
| 	if flags&cPIPE_TYPE_MESSAGE != 0 { | ||||
| 	if flags&windows.PIPE_TYPE_MESSAGE != 0 { | ||||
| 		return &win32MessageBytePipe{ | ||||
| 			win32Pipe: win32Pipe{win32File: f, path: path}, | ||||
| 		}, nil | ||||
|  | @ -283,7 +270,11 @@ func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (sy | |||
| 	oa.Length = unsafe.Sizeof(oa) | ||||
| 
 | ||||
| 	var ntPath unicodeString | ||||
| 	if err := rtlDosPathNameToNtPathName(&path16[0], &ntPath, 0, 0).Err(); err != nil { | ||||
| 	if err := rtlDosPathNameToNtPathName(&path16[0], | ||||
| 		&ntPath, | ||||
| 		0, | ||||
| 		0, | ||||
| 	).Err(); err != nil { | ||||
| 		return 0, &os.PathError{Op: "open", Path: path, Err: err} | ||||
| 	} | ||||
| 	defer localFree(ntPath.Buffer) | ||||
|  | @ -292,8 +283,8 @@ func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (sy | |||
| 	// The security descriptor is only needed for the first pipe.
 | ||||
| 	if first { | ||||
| 		if sd != nil { | ||||
| 			len := uint32(len(sd)) | ||||
| 			sdb := localAlloc(0, len) | ||||
| 			l := uint32(len(sd)) | ||||
| 			sdb := localAlloc(0, l) | ||||
| 			defer localFree(sdb) | ||||
| 			copy((*[0xffff]byte)(unsafe.Pointer(sdb))[:], sd) | ||||
| 			oa.SecurityDescriptor = (*securityDescriptor)(unsafe.Pointer(sdb)) | ||||
|  | @ -301,28 +292,28 @@ func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (sy | |||
| 			// Construct the default named pipe security descriptor.
 | ||||
| 			var dacl uintptr | ||||
| 			if err := rtlDefaultNpAcl(&dacl).Err(); err != nil { | ||||
| 				return 0, fmt.Errorf("getting default named pipe ACL: %s", err) | ||||
| 				return 0, fmt.Errorf("getting default named pipe ACL: %w", err) | ||||
| 			} | ||||
| 			defer localFree(dacl) | ||||
| 
 | ||||
| 			sdb := &securityDescriptor{ | ||||
| 				Revision: 1, | ||||
| 				Control:  cSE_DACL_PRESENT, | ||||
| 				Control:  windows.SE_DACL_PRESENT, | ||||
| 				Dacl:     dacl, | ||||
| 			} | ||||
| 			oa.SecurityDescriptor = sdb | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	typ := uint32(cFILE_PIPE_REJECT_REMOTE_CLIENTS) | ||||
| 	typ := uint32(windows.FILE_PIPE_REJECT_REMOTE_CLIENTS) | ||||
| 	if c.MessageMode { | ||||
| 		typ |= cFILE_PIPE_MESSAGE_TYPE | ||||
| 		typ |= windows.FILE_PIPE_MESSAGE_TYPE | ||||
| 	} | ||||
| 
 | ||||
| 	disposition := uint32(cFILE_OPEN) | ||||
| 	disposition := uint32(windows.FILE_OPEN) | ||||
| 	access := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE | syscall.SYNCHRONIZE) | ||||
| 	if first { | ||||
| 		disposition = cFILE_CREATE | ||||
| 		disposition = windows.FILE_CREATE | ||||
| 		// By not asking for read or write access, the named pipe file system
 | ||||
| 		// will put this pipe into an initially disconnected state, blocking
 | ||||
| 		// client connections until the next call with first == false.
 | ||||
|  | @ -335,7 +326,20 @@ func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (sy | |||
| 		h    syscall.Handle | ||||
| 		iosb ioStatusBlock | ||||
| 	) | ||||
| 	err = ntCreateNamedPipeFile(&h, access, &oa, &iosb, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE, disposition, 0, typ, 0, 0, 0xffffffff, uint32(c.InputBufferSize), uint32(c.OutputBufferSize), &timeout).Err() | ||||
| 	err = ntCreateNamedPipeFile(&h, | ||||
| 		access, | ||||
| 		&oa, | ||||
| 		&iosb, | ||||
| 		syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE, | ||||
| 		disposition, | ||||
| 		0, | ||||
| 		typ, | ||||
| 		0, | ||||
| 		0, | ||||
| 		0xffffffff, | ||||
| 		uint32(c.InputBufferSize), | ||||
| 		uint32(c.OutputBufferSize), | ||||
| 		&timeout).Err() | ||||
| 	if err != nil { | ||||
| 		return 0, &os.PathError{Op: "open", Path: path, Err: err} | ||||
| 	} | ||||
|  | @ -380,7 +384,7 @@ func (l *win32PipeListener) makeConnectedServerPipe() (*win32File, error) { | |||
| 		p.Close() | ||||
| 		p = nil | ||||
| 		err = <-ch | ||||
| 		if err == nil || err == ErrFileClosed { | ||||
| 		if err == nil || err == ErrFileClosed { //nolint:errorlint // err is Errno
 | ||||
| 			err = ErrPipeListenerClosed | ||||
| 		} | ||||
| 	} | ||||
|  | @ -402,12 +406,12 @@ func (l *win32PipeListener) listenerRoutine() { | |||
| 				p, err = l.makeConnectedServerPipe() | ||||
| 				// If the connection was immediately closed by the client, try
 | ||||
| 				// again.
 | ||||
| 				if err != cERROR_NO_DATA { | ||||
| 				if err != windows.ERROR_NO_DATA { //nolint:errorlint // err is Errno
 | ||||
| 					break | ||||
| 				} | ||||
| 			} | ||||
| 			responseCh <- acceptResponse{p, err} | ||||
| 			closed = err == ErrPipeListenerClosed | ||||
| 			closed = err == ErrPipeListenerClosed //nolint:errorlint // err is Errno
 | ||||
| 		} | ||||
| 	} | ||||
| 	syscall.Close(l.firstHandle) | ||||
|  | @ -469,15 +473,15 @@ func ListenPipe(path string, c *PipeConfig) (net.Listener, error) { | |||
| } | ||||
| 
 | ||||
| func connectPipe(p *win32File) error { | ||||
| 	c, err := p.prepareIo() | ||||
| 	c, err := p.prepareIO() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	defer p.wg.Done() | ||||
| 
 | ||||
| 	err = connectNamedPipe(p.handle, &c.o) | ||||
| 	_, err = p.asyncIo(c, nil, 0, err) | ||||
| 	if err != nil && err != cERROR_PIPE_CONNECTED { | ||||
| 	_, err = p.asyncIO(c, nil, 0, err) | ||||
| 	if err != nil && err != windows.ERROR_PIPE_CONNECTED { //nolint:errorlint // err is Errno
 | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
|  |  | |||
|  | @ -1,5 +1,3 @@ | |||
| // +build windows
 | ||||
| 
 | ||||
| // Package guid provides a GUID type. The backing structure for a GUID is
 | ||||
| // identical to that used by the golang.org/x/sys/windows GUID type.
 | ||||
| // There are two main binary encodings used for a GUID, the big-endian encoding,
 | ||||
|  | @ -9,24 +7,26 @@ package guid | |||
| 
 | ||||
| import ( | ||||
| 	"crypto/rand" | ||||
| 	"crypto/sha1" | ||||
| 	"crypto/sha1" //nolint:gosec // not used for secure application
 | ||||
| 	"encoding" | ||||
| 	"encoding/binary" | ||||
| 	"fmt" | ||||
| 	"strconv" | ||||
| ) | ||||
| 
 | ||||
| //go:generate go run golang.org/x/tools/cmd/stringer -type=Variant -trimprefix=Variant -linecomment
 | ||||
| 
 | ||||
| // Variant specifies which GUID variant (or "type") of the GUID. It determines
 | ||||
| // how the entirety of the rest of the GUID is interpreted.
 | ||||
| type Variant uint8 | ||||
| 
 | ||||
| // The variants specified by RFC 4122.
 | ||||
| // The variants specified by RFC 4122 section 4.1.1.
 | ||||
| const ( | ||||
| 	// VariantUnknown specifies a GUID variant which does not conform to one of
 | ||||
| 	// the variant encodings specified in RFC 4122.
 | ||||
| 	VariantUnknown Variant = iota | ||||
| 	VariantNCS | ||||
| 	VariantRFC4122 | ||||
| 	VariantRFC4122 // RFC 4122
 | ||||
| 	VariantMicrosoft | ||||
| 	VariantFuture | ||||
| ) | ||||
|  | @ -36,6 +36,10 @@ const ( | |||
| // hash of an input string.
 | ||||
| type Version uint8 | ||||
| 
 | ||||
| func (v Version) String() string { | ||||
| 	return strconv.FormatUint(uint64(v), 10) | ||||
| } | ||||
| 
 | ||||
| var _ = (encoding.TextMarshaler)(GUID{}) | ||||
| var _ = (encoding.TextUnmarshaler)(&GUID{}) | ||||
| 
 | ||||
|  | @ -61,7 +65,7 @@ func NewV4() (GUID, error) { | |||
| // big-endian UTF16 stream of bytes. If that is desired, the string can be
 | ||||
| // encoded as such before being passed to this function.
 | ||||
| func NewV5(namespace GUID, name []byte) (GUID, error) { | ||||
| 	b := sha1.New() | ||||
| 	b := sha1.New() //nolint:gosec // not used for secure application
 | ||||
| 	namespaceBytes := namespace.ToArray() | ||||
| 	b.Write(namespaceBytes[:]) | ||||
| 	b.Write(name) | ||||
|  |  | |||
|  | @ -1,3 +1,4 @@ | |||
| //go:build !windows
 | ||||
| // +build !windows
 | ||||
| 
 | ||||
| package guid | ||||
|  |  | |||
|  | @ -1,3 +1,6 @@ | |||
| //go:build windows
 | ||||
| // +build windows
 | ||||
| 
 | ||||
| package guid | ||||
| 
 | ||||
| import "golang.org/x/sys/windows" | ||||
|  |  | |||
|  | @ -0,0 +1,27 @@ | |||
| // Code generated by "stringer -type=Variant -trimprefix=Variant -linecomment"; DO NOT EDIT.
 | ||||
| 
 | ||||
| package guid | ||||
| 
 | ||||
| import "strconv" | ||||
| 
 | ||||
| func _() { | ||||
| 	// An "invalid array index" compiler error signifies that the constant values have changed.
 | ||||
| 	// Re-run the stringer command to generate them again.
 | ||||
| 	var x [1]struct{} | ||||
| 	_ = x[VariantUnknown-0] | ||||
| 	_ = x[VariantNCS-1] | ||||
| 	_ = x[VariantRFC4122-2] | ||||
| 	_ = x[VariantMicrosoft-3] | ||||
| 	_ = x[VariantFuture-4] | ||||
| } | ||||
| 
 | ||||
| const _Variant_name = "UnknownNCSRFC 4122MicrosoftFuture" | ||||
| 
 | ||||
| var _Variant_index = [...]uint8{0, 7, 10, 18, 27, 33} | ||||
| 
 | ||||
| func (i Variant) String() string { | ||||
| 	if i >= Variant(len(_Variant_index)-1) { | ||||
| 		return "Variant(" + strconv.FormatInt(int64(i), 10) + ")" | ||||
| 	} | ||||
| 	return _Variant_name[_Variant_index[i]:_Variant_index[i+1]] | ||||
| } | ||||
|  | @ -1,3 +1,4 @@ | |||
| //go:build windows
 | ||||
| // +build windows
 | ||||
| 
 | ||||
| package security | ||||
|  | @ -20,6 +21,7 @@ type ( | |||
| 	trusteeForm         uint32 | ||||
| 	trusteeType         uint32 | ||||
| 
 | ||||
| 	//nolint:structcheck // structcheck thinks fields are unused, but the are used to pass data to OS
 | ||||
| 	explicitAccess struct { | ||||
| 		accessPermissions accessMask | ||||
| 		accessMode        accessMode | ||||
|  | @ -27,6 +29,7 @@ type ( | |||
| 		trustee           trustee | ||||
| 	} | ||||
| 
 | ||||
| 	//nolint:structcheck,unused // structcheck thinks fields are unused, but the are used to pass data to OS
 | ||||
| 	trustee struct { | ||||
| 		multipleTrustee          *trustee | ||||
| 		multipleTrusteeOperation int32 | ||||
|  | @ -44,6 +47,7 @@ const ( | |||
| 	desiredAccessReadControl desiredAccess = 0x20000 | ||||
| 	desiredAccessWriteDac    desiredAccess = 0x40000 | ||||
| 
 | ||||
| 	//cspell:disable-next-line
 | ||||
| 	gvmga = "GrantVmGroupAccess:" | ||||
| 
 | ||||
| 	inheritModeNoInheritance                  inheritMode = 0x0 | ||||
|  | @ -56,9 +60,9 @@ const ( | |||
| 	shareModeRead  shareMode = 0x1 | ||||
| 	shareModeWrite shareMode = 0x2 | ||||
| 
 | ||||
| 	sidVmGroup = "S-1-5-83-0" | ||||
| 	sidVMGroup = "S-1-5-83-0" | ||||
| 
 | ||||
| 	trusteeFormIsSid trusteeForm = 0 | ||||
| 	trusteeFormIsSID trusteeForm = 0 | ||||
| 
 | ||||
| 	trusteeTypeWellKnownGroup trusteeType = 5 | ||||
| ) | ||||
|  | @ -67,6 +71,8 @@ const ( | |||
| // include Grant ACE entries for the VM Group SID. This is a golang re-
 | ||||
| // implementation of the same function in vmcompute, just not exported in
 | ||||
| // RS5. Which kind of sucks. Sucks a lot :/
 | ||||
| //
 | ||||
| //revive:disable-next-line:var-naming VM, not Vm
 | ||||
| func GrantVmGroupAccess(name string) error { | ||||
| 	// Stat (to determine if `name` is a directory).
 | ||||
| 	s, err := os.Stat(name) | ||||
|  | @ -79,7 +85,7 @@ func GrantVmGroupAccess(name string) error { | |||
| 	if err != nil { | ||||
| 		return err // Already wrapped
 | ||||
| 	} | ||||
| 	defer syscall.CloseHandle(fd) | ||||
| 	defer syscall.CloseHandle(fd) //nolint:errcheck
 | ||||
| 
 | ||||
| 	// Get the current DACL and Security Descriptor. Must defer LocalFree on success.
 | ||||
| 	ot := objectTypeFileObject | ||||
|  | @ -89,7 +95,7 @@ func GrantVmGroupAccess(name string) error { | |||
| 	if err := getSecurityInfo(fd, uint32(ot), uint32(si), nil, nil, &origDACL, nil, &sd); err != nil { | ||||
| 		return fmt.Errorf("%s GetSecurityInfo %s: %w", gvmga, name, err) | ||||
| 	} | ||||
| 	defer syscall.LocalFree((syscall.Handle)(unsafe.Pointer(sd))) | ||||
| 	defer syscall.LocalFree((syscall.Handle)(unsafe.Pointer(sd))) //nolint:errcheck
 | ||||
| 
 | ||||
| 	// Generate a new DACL which is the current DACL with the required ACEs added.
 | ||||
| 	// Must defer LocalFree on success.
 | ||||
|  | @ -97,7 +103,7 @@ func GrantVmGroupAccess(name string) error { | |||
| 	if err != nil { | ||||
| 		return err // Already wrapped
 | ||||
| 	} | ||||
| 	defer syscall.LocalFree((syscall.Handle)(unsafe.Pointer(newDACL))) | ||||
| 	defer syscall.LocalFree((syscall.Handle)(unsafe.Pointer(newDACL))) //nolint:errcheck
 | ||||
| 
 | ||||
| 	// And finally use SetSecurityInfo to apply the updated DACL.
 | ||||
| 	if err := setSecurityInfo(fd, uint32(ot), uint32(si), uintptr(0), uintptr(0), newDACL, uintptr(0)); err != nil { | ||||
|  | @ -110,16 +116,19 @@ func GrantVmGroupAccess(name string) error { | |||
| // createFile is a helper function to call [Nt]CreateFile to get a handle to
 | ||||
| // the file or directory.
 | ||||
| func createFile(name string, isDir bool) (syscall.Handle, error) { | ||||
| 	namep := syscall.StringToUTF16(name) | ||||
| 	namep, err := syscall.UTF16FromString(name) | ||||
| 	if err != nil { | ||||
| 		return syscall.InvalidHandle, fmt.Errorf("could not convernt name to UTF-16: %w", err) | ||||
| 	} | ||||
| 	da := uint32(desiredAccessReadControl | desiredAccessWriteDac) | ||||
| 	sm := uint32(shareModeRead | shareModeWrite) | ||||
| 	fa := uint32(syscall.FILE_ATTRIBUTE_NORMAL) | ||||
| 	if isDir { | ||||
| 		fa = uint32(fa | syscall.FILE_FLAG_BACKUP_SEMANTICS) | ||||
| 		fa |= syscall.FILE_FLAG_BACKUP_SEMANTICS | ||||
| 	} | ||||
| 	fd, err := syscall.CreateFile(&namep[0], da, sm, nil, syscall.OPEN_EXISTING, fa, 0) | ||||
| 	if err != nil { | ||||
| 		return 0, fmt.Errorf("%s syscall.CreateFile %s: %w", gvmga, name, err) | ||||
| 		return syscall.InvalidHandle, fmt.Errorf("%s syscall.CreateFile %s: %w", gvmga, name, err) | ||||
| 	} | ||||
| 	return fd, nil | ||||
| } | ||||
|  | @ -128,9 +137,9 @@ func createFile(name string, isDir bool) (syscall.Handle, error) { | |||
| // The caller is responsible for LocalFree of the returned DACL on success.
 | ||||
| func generateDACLWithAcesAdded(name string, isDir bool, origDACL uintptr) (uintptr, error) { | ||||
| 	// Generate pointers to the SIDs based on the string SIDs
 | ||||
| 	sid, err := syscall.StringToSid(sidVmGroup) | ||||
| 	sid, err := syscall.StringToSid(sidVMGroup) | ||||
| 	if err != nil { | ||||
| 		return 0, fmt.Errorf("%s syscall.StringToSid %s %s: %w", gvmga, name, sidVmGroup, err) | ||||
| 		return 0, fmt.Errorf("%s syscall.StringToSid %s %s: %w", gvmga, name, sidVMGroup, err) | ||||
| 	} | ||||
| 
 | ||||
| 	inheritance := inheritModeNoInheritance | ||||
|  | @ -139,12 +148,12 @@ func generateDACLWithAcesAdded(name string, isDir bool, origDACL uintptr) (uintp | |||
| 	} | ||||
| 
 | ||||
| 	eaArray := []explicitAccess{ | ||||
| 		explicitAccess{ | ||||
| 		{ | ||||
| 			accessPermissions: accessMaskDesiredPermission, | ||||
| 			accessMode:        accessModeGrant, | ||||
| 			inheritance:       inheritance, | ||||
| 			trustee: trustee{ | ||||
| 				trusteeForm: trusteeFormIsSid, | ||||
| 				trusteeForm: trusteeFormIsSID, | ||||
| 				trusteeType: trusteeTypeWellKnownGroup, | ||||
| 				name:        uintptr(unsafe.Pointer(sid)), | ||||
| 			}, | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| package security | ||||
| 
 | ||||
| //go:generate go run mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go
 | ||||
| //go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go syscall_windows.go
 | ||||
| 
 | ||||
| //sys getSecurityInfo(handle syscall.Handle, objectType uint32, si uint32, ppsidOwner **uintptr, ppsidGroup **uintptr, ppDacl *uintptr, ppSacl *uintptr, ppSecurityDescriptor *uintptr) (win32err error) = advapi32.GetSecurityInfo
 | ||||
| //sys setSecurityInfo(handle syscall.Handle, objectType uint32, si uint32, psidOwner uintptr, psidGroup uintptr, pDacl uintptr, pSacl uintptr) (win32err error) = advapi32.SetSecurityInfo
 | ||||
|  |  | |||
|  | @ -1,4 +1,6 @@ | |||
| // Code generated by 'go generate'; DO NOT EDIT.
 | ||||
| //go:build windows
 | ||||
| 
 | ||||
| // Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT.
 | ||||
| 
 | ||||
| package security | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,3 +1,4 @@ | |||
| //go:build windows
 | ||||
| // +build windows
 | ||||
| 
 | ||||
| package winio | ||||
|  | @ -24,22 +25,17 @@ import ( | |||
| //sys lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) = advapi32.LookupPrivilegeDisplayNameW
 | ||||
| 
 | ||||
| const ( | ||||
| 	SE_PRIVILEGE_ENABLED = 2 | ||||
| 	//revive:disable-next-line:var-naming ALL_CAPS
 | ||||
| 	SE_PRIVILEGE_ENABLED = windows.SE_PRIVILEGE_ENABLED | ||||
| 
 | ||||
| 	ERROR_NOT_ALL_ASSIGNED syscall.Errno = 1300 | ||||
| 	//revive:disable-next-line:var-naming ALL_CAPS
 | ||||
| 	ERROR_NOT_ALL_ASSIGNED syscall.Errno = windows.ERROR_NOT_ALL_ASSIGNED | ||||
| 
 | ||||
| 	SeBackupPrivilege   = "SeBackupPrivilege" | ||||
| 	SeRestorePrivilege  = "SeRestorePrivilege" | ||||
| 	SeSecurityPrivilege = "SeSecurityPrivilege" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	securityAnonymous = iota | ||||
| 	securityIdentification | ||||
| 	securityImpersonation | ||||
| 	securityDelegation | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	privNames     = make(map[string]uint64) | ||||
| 	privNameMutex sync.Mutex | ||||
|  | @ -51,11 +47,9 @@ type PrivilegeError struct { | |||
| } | ||||
| 
 | ||||
| func (e *PrivilegeError) Error() string { | ||||
| 	s := "" | ||||
| 	s := "Could not enable privilege " | ||||
| 	if len(e.privileges) > 1 { | ||||
| 		s = "Could not enable privileges " | ||||
| 	} else { | ||||
| 		s = "Could not enable privilege " | ||||
| 	} | ||||
| 	for i, p := range e.privileges { | ||||
| 		if i != 0 { | ||||
|  | @ -94,7 +88,7 @@ func RunWithPrivileges(names []string, fn func() error) error { | |||
| } | ||||
| 
 | ||||
| func mapPrivileges(names []string) ([]uint64, error) { | ||||
| 	var privileges []uint64 | ||||
| 	privileges := make([]uint64, 0, len(names)) | ||||
| 	privNameMutex.Lock() | ||||
| 	defer privNameMutex.Unlock() | ||||
| 	for _, name := range names { | ||||
|  | @ -127,7 +121,7 @@ func enableDisableProcessPrivilege(names []string, action uint32) error { | |||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	p, _ := windows.GetCurrentProcess() | ||||
| 	p := windows.CurrentProcess() | ||||
| 	var token windows.Token | ||||
| 	err = windows.OpenProcessToken(p, windows.TOKEN_ADJUST_PRIVILEGES|windows.TOKEN_QUERY, &token) | ||||
| 	if err != nil { | ||||
|  | @ -140,10 +134,10 @@ func enableDisableProcessPrivilege(names []string, action uint32) error { | |||
| 
 | ||||
| func adjustPrivileges(token windows.Token, privileges []uint64, action uint32) error { | ||||
| 	var b bytes.Buffer | ||||
| 	binary.Write(&b, binary.LittleEndian, uint32(len(privileges))) | ||||
| 	_ = binary.Write(&b, binary.LittleEndian, uint32(len(privileges))) | ||||
| 	for _, p := range privileges { | ||||
| 		binary.Write(&b, binary.LittleEndian, p) | ||||
| 		binary.Write(&b, binary.LittleEndian, action) | ||||
| 		_ = binary.Write(&b, binary.LittleEndian, p) | ||||
| 		_ = binary.Write(&b, binary.LittleEndian, action) | ||||
| 	} | ||||
| 	prevState := make([]byte, b.Len()) | ||||
| 	reqSize := uint32(0) | ||||
|  | @ -151,7 +145,7 @@ func adjustPrivileges(token windows.Token, privileges []uint64, action uint32) e | |||
| 	if !success { | ||||
| 		return err | ||||
| 	} | ||||
| 	if err == ERROR_NOT_ALL_ASSIGNED { | ||||
| 	if err == ERROR_NOT_ALL_ASSIGNED { //nolint:errorlint // err is Errno
 | ||||
| 		return &PrivilegeError{privileges} | ||||
| 	} | ||||
| 	return nil | ||||
|  | @ -177,7 +171,7 @@ func getPrivilegeName(luid uint64) string { | |||
| } | ||||
| 
 | ||||
| func newThreadToken() (windows.Token, error) { | ||||
| 	err := impersonateSelf(securityImpersonation) | ||||
| 	err := impersonateSelf(windows.SecurityImpersonation) | ||||
| 	if err != nil { | ||||
| 		return 0, err | ||||
| 	} | ||||
|  |  | |||
|  | @ -1,3 +1,6 @@ | |||
| //go:build windows
 | ||||
| // +build windows
 | ||||
| 
 | ||||
| package winio | ||||
| 
 | ||||
| import ( | ||||
|  | @ -113,16 +116,16 @@ func EncodeReparsePoint(rp *ReparsePoint) []byte { | |||
| 	} | ||||
| 
 | ||||
| 	var b bytes.Buffer | ||||
| 	binary.Write(&b, binary.LittleEndian, &data) | ||||
| 	_ = binary.Write(&b, binary.LittleEndian, &data) | ||||
| 	if !rp.IsMountPoint { | ||||
| 		flags := uint32(0) | ||||
| 		if relative { | ||||
| 			flags |= 1 | ||||
| 		} | ||||
| 		binary.Write(&b, binary.LittleEndian, flags) | ||||
| 		_ = binary.Write(&b, binary.LittleEndian, flags) | ||||
| 	} | ||||
| 
 | ||||
| 	binary.Write(&b, binary.LittleEndian, ntTarget16) | ||||
| 	binary.Write(&b, binary.LittleEndian, target16) | ||||
| 	_ = binary.Write(&b, binary.LittleEndian, ntTarget16) | ||||
| 	_ = binary.Write(&b, binary.LittleEndian, target16) | ||||
| 	return b.Bytes() | ||||
| } | ||||
|  |  | |||
|  | @ -1,23 +1,25 @@ | |||
| //go:build windows
 | ||||
| // +build windows
 | ||||
| 
 | ||||
| package winio | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"syscall" | ||||
| 	"unsafe" | ||||
| 
 | ||||
| 	"golang.org/x/sys/windows" | ||||
| ) | ||||
| 
 | ||||
| //sys lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountNameW
 | ||||
| //sys lookupAccountSid(systemName *uint16, sid *byte, name *uint16, nameSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountSidW
 | ||||
| //sys convertSidToStringSid(sid *byte, str **uint16) (err error) = advapi32.ConvertSidToStringSidW
 | ||||
| //sys convertStringSidToSid(str *uint16, sid **byte) (err error) = advapi32.ConvertStringSidToSidW
 | ||||
| //sys convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) = advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW
 | ||||
| //sys convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) = advapi32.ConvertSecurityDescriptorToStringSecurityDescriptorW
 | ||||
| //sys localFree(mem uintptr) = LocalFree
 | ||||
| //sys getSecurityDescriptorLength(sd uintptr) (len uint32) = advapi32.GetSecurityDescriptorLength
 | ||||
| 
 | ||||
| const ( | ||||
| 	cERROR_NONE_MAPPED = syscall.Errno(1332) | ||||
| ) | ||||
| 
 | ||||
| type AccountLookupError struct { | ||||
| 	Name string | ||||
| 	Err  error | ||||
|  | @ -28,8 +30,10 @@ func (e *AccountLookupError) Error() string { | |||
| 		return "lookup account: empty account name specified" | ||||
| 	} | ||||
| 	var s string | ||||
| 	switch e.Err { | ||||
| 	case cERROR_NONE_MAPPED: | ||||
| 	switch { | ||||
| 	case errors.Is(e.Err, windows.ERROR_INVALID_SID): | ||||
| 		s = "the security ID structure is invalid" | ||||
| 	case errors.Is(e.Err, windows.ERROR_NONE_MAPPED): | ||||
| 		s = "not found" | ||||
| 	default: | ||||
| 		s = e.Err.Error() | ||||
|  | @ -37,6 +41,8 @@ func (e *AccountLookupError) Error() string { | |||
| 	return "lookup account " + e.Name + ": " + s | ||||
| } | ||||
| 
 | ||||
| func (e *AccountLookupError) Unwrap() error { return e.Err } | ||||
| 
 | ||||
| type SddlConversionError struct { | ||||
| 	Sddl string | ||||
| 	Err  error | ||||
|  | @ -46,15 +52,19 @@ func (e *SddlConversionError) Error() string { | |||
| 	return "convert " + e.Sddl + ": " + e.Err.Error() | ||||
| } | ||||
| 
 | ||||
| func (e *SddlConversionError) Unwrap() error { return e.Err } | ||||
| 
 | ||||
| // LookupSidByName looks up the SID of an account by name
 | ||||
| //
 | ||||
| //revive:disable-next-line:var-naming SID, not Sid
 | ||||
| func LookupSidByName(name string) (sid string, err error) { | ||||
| 	if name == "" { | ||||
| 		return "", &AccountLookupError{name, cERROR_NONE_MAPPED} | ||||
| 		return "", &AccountLookupError{name, windows.ERROR_NONE_MAPPED} | ||||
| 	} | ||||
| 
 | ||||
| 	var sidSize, sidNameUse, refDomainSize uint32 | ||||
| 	err = lookupAccountName(nil, name, nil, &sidSize, nil, &refDomainSize, &sidNameUse) | ||||
| 	if err != nil && err != syscall.ERROR_INSUFFICIENT_BUFFER { | ||||
| 	if err != nil && err != syscall.ERROR_INSUFFICIENT_BUFFER { //nolint:errorlint // err is Errno
 | ||||
| 		return "", &AccountLookupError{name, err} | ||||
| 	} | ||||
| 	sidBuffer := make([]byte, sidSize) | ||||
|  | @ -73,6 +83,42 @@ func LookupSidByName(name string) (sid string, err error) { | |||
| 	return sid, nil | ||||
| } | ||||
| 
 | ||||
| // LookupNameBySid looks up the name of an account by SID
 | ||||
| //
 | ||||
| //revive:disable-next-line:var-naming SID, not Sid
 | ||||
| func LookupNameBySid(sid string) (name string, err error) { | ||||
| 	if sid == "" { | ||||
| 		return "", &AccountLookupError{sid, windows.ERROR_NONE_MAPPED} | ||||
| 	} | ||||
| 
 | ||||
| 	sidBuffer, err := windows.UTF16PtrFromString(sid) | ||||
| 	if err != nil { | ||||
| 		return "", &AccountLookupError{sid, err} | ||||
| 	} | ||||
| 
 | ||||
| 	var sidPtr *byte | ||||
| 	if err = convertStringSidToSid(sidBuffer, &sidPtr); err != nil { | ||||
| 		return "", &AccountLookupError{sid, err} | ||||
| 	} | ||||
| 	defer localFree(uintptr(unsafe.Pointer(sidPtr))) | ||||
| 
 | ||||
| 	var nameSize, refDomainSize, sidNameUse uint32 | ||||
| 	err = lookupAccountSid(nil, sidPtr, nil, &nameSize, nil, &refDomainSize, &sidNameUse) | ||||
| 	if err != nil && err != windows.ERROR_INSUFFICIENT_BUFFER { //nolint:errorlint // err is Errno
 | ||||
| 		return "", &AccountLookupError{sid, err} | ||||
| 	} | ||||
| 
 | ||||
| 	nameBuffer := make([]uint16, nameSize) | ||||
| 	refDomainBuffer := make([]uint16, refDomainSize) | ||||
| 	err = lookupAccountSid(nil, sidPtr, &nameBuffer[0], &nameSize, &refDomainBuffer[0], &refDomainSize, &sidNameUse) | ||||
| 	if err != nil { | ||||
| 		return "", &AccountLookupError{sid, err} | ||||
| 	} | ||||
| 
 | ||||
| 	name = windows.UTF16ToString(nameBuffer) | ||||
| 	return name, nil | ||||
| } | ||||
| 
 | ||||
| func SddlToSecurityDescriptor(sddl string) ([]byte, error) { | ||||
| 	var sdBuffer uintptr | ||||
| 	err := convertStringSecurityDescriptorToSecurityDescriptor(sddl, 1, &sdBuffer, nil) | ||||
|  | @ -87,7 +133,7 @@ func SddlToSecurityDescriptor(sddl string) ([]byte, error) { | |||
| 
 | ||||
| func SecurityDescriptorToSddl(sd []byte) (string, error) { | ||||
| 	var sddl *uint16 | ||||
| 	// The returned string length seems to including an aribtrary number of terminating NULs.
 | ||||
| 	// The returned string length seems to include an arbitrary number of terminating NULs.
 | ||||
| 	// Don't use it.
 | ||||
| 	err := convertSecurityDescriptorToStringSecurityDescriptor(&sd[0], 1, 0xff, &sddl, nil) | ||||
| 	if err != nil { | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| //go:build windows
 | ||||
| 
 | ||||
| package winio | ||||
| 
 | ||||
| //go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go file.go pipe.go sd.go fileinfo.go privilege.go backup.go hvsock.go
 | ||||
| //go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go ./*.go
 | ||||
|  |  | |||
|  | @ -0,0 +1,5 @@ | |||
| //go:build tools
 | ||||
| 
 | ||||
| package winio | ||||
| 
 | ||||
| import _ "golang.org/x/tools/cmd/stringer" | ||||
|  | @ -11,7 +11,7 @@ import ( | |||
| 	"golang.org/x/sys/windows" | ||||
| ) | ||||
| 
 | ||||
| //go:generate go run mksyscall_windows.go -output zvhd_windows.go vhd.go
 | ||||
| //go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zvhd_windows.go vhd.go
 | ||||
| 
 | ||||
| //sys createVirtualDisk(virtualStorageType *VirtualStorageType, path string, virtualDiskAccessMask uint32, securityDescriptor *uintptr, createVirtualDiskFlags uint32, providerSpecificFlags uint32, parameters *CreateVirtualDiskParameters, overlapped *syscall.Overlapped, handle *syscall.Handle) (win32err error) = virtdisk.CreateVirtualDisk
 | ||||
| //sys openVirtualDisk(virtualStorageType *VirtualStorageType, path string, virtualDiskAccessMask uint32, openVirtualDiskFlags uint32, parameters *openVirtualDiskParameters, handle *syscall.Handle) (win32err error) = virtdisk.OpenVirtualDisk
 | ||||
|  | @ -62,8 +62,8 @@ type OpenVirtualDiskParameters struct { | |||
| 	Version2 OpenVersion2 | ||||
| } | ||||
| 
 | ||||
| // The higher level `OpenVersion2` struct uses bools to refer to `GetInfoOnly` and `ReadOnly` for ease of use. However,
 | ||||
| // the internal windows structure uses `BOOLS` aka int32s for these types. `openVersion2` is used for translating
 | ||||
| // The higher level `OpenVersion2` struct uses `bool`s to refer to `GetInfoOnly` and `ReadOnly` for ease of use. However,
 | ||||
| // the internal windows structure uses `BOOL`s aka int32s for these types. `openVersion2` is used for translating
 | ||||
| // `OpenVersion2` fields to the correct windows internal field types on the `Open____` methods.
 | ||||
| type openVersion2 struct { | ||||
| 	getInfoOnly    int32 | ||||
|  | @ -87,9 +87,10 @@ type AttachVirtualDiskParameters struct { | |||
| } | ||||
| 
 | ||||
| const ( | ||||
| 	//revive:disable-next-line:var-naming ALL_CAPS
 | ||||
| 	VIRTUAL_STORAGE_TYPE_DEVICE_VHDX = 0x3 | ||||
| 
 | ||||
| 	// Access Mask for opening a VHD
 | ||||
| 	// Access Mask for opening a VHD.
 | ||||
| 	VirtualDiskAccessNone     VirtualDiskAccessMask = 0x00000000 | ||||
| 	VirtualDiskAccessAttachRO VirtualDiskAccessMask = 0x00010000 | ||||
| 	VirtualDiskAccessAttachRW VirtualDiskAccessMask = 0x00020000 | ||||
|  | @ -101,7 +102,7 @@ const ( | |||
| 	VirtualDiskAccessAll      VirtualDiskAccessMask = 0x003f0000 | ||||
| 	VirtualDiskAccessWritable VirtualDiskAccessMask = 0x00320000 | ||||
| 
 | ||||
| 	// Flags for creating a VHD
 | ||||
| 	// Flags for creating a VHD.
 | ||||
| 	CreateVirtualDiskFlagNone                              CreateVirtualDiskFlag = 0x0 | ||||
| 	CreateVirtualDiskFlagFullPhysicalAllocation            CreateVirtualDiskFlag = 0x1 | ||||
| 	CreateVirtualDiskFlagPreventWritesToSourceDisk         CreateVirtualDiskFlag = 0x2 | ||||
|  | @ -109,12 +110,12 @@ const ( | |||
| 	CreateVirtualDiskFlagCreateBackingStorage              CreateVirtualDiskFlag = 0x8 | ||||
| 	CreateVirtualDiskFlagUseChangeTrackingSourceLimit      CreateVirtualDiskFlag = 0x10 | ||||
| 	CreateVirtualDiskFlagPreserveParentChangeTrackingState CreateVirtualDiskFlag = 0x20 | ||||
| 	CreateVirtualDiskFlagVhdSetUseOriginalBackingStorage   CreateVirtualDiskFlag = 0x40 | ||||
| 	CreateVirtualDiskFlagVhdSetUseOriginalBackingStorage   CreateVirtualDiskFlag = 0x40 //revive:disable-line:var-naming VHD, not Vhd
 | ||||
| 	CreateVirtualDiskFlagSparseFile                        CreateVirtualDiskFlag = 0x80 | ||||
| 	CreateVirtualDiskFlagPmemCompatible                    CreateVirtualDiskFlag = 0x100 | ||||
| 	CreateVirtualDiskFlagPmemCompatible                    CreateVirtualDiskFlag = 0x100 //revive:disable-line:var-naming PMEM, not Pmem
 | ||||
| 	CreateVirtualDiskFlagSupportCompressedVolumes          CreateVirtualDiskFlag = 0x200 | ||||
| 
 | ||||
| 	// Flags for opening a VHD
 | ||||
| 	// Flags for opening a VHD.
 | ||||
| 	OpenVirtualDiskFlagNone                        VirtualDiskFlag = 0x00000000 | ||||
| 	OpenVirtualDiskFlagNoParents                   VirtualDiskFlag = 0x00000001 | ||||
| 	OpenVirtualDiskFlagBlankFile                   VirtualDiskFlag = 0x00000002 | ||||
|  | @ -127,7 +128,7 @@ const ( | |||
| 	OpenVirtualDiskFlagNoWriteHardening            VirtualDiskFlag = 0x00000100 | ||||
| 	OpenVirtualDiskFlagSupportCompressedVolumes    VirtualDiskFlag = 0x00000200 | ||||
| 
 | ||||
| 	// Flags for attaching a VHD
 | ||||
| 	// Flags for attaching a VHD.
 | ||||
| 	AttachVirtualDiskFlagNone                          AttachVirtualDiskFlag = 0x00000000 | ||||
| 	AttachVirtualDiskFlagReadOnly                      AttachVirtualDiskFlag = 0x00000001 | ||||
| 	AttachVirtualDiskFlagNoDriveLetter                 AttachVirtualDiskFlag = 0x00000002 | ||||
|  | @ -140,12 +141,14 @@ const ( | |||
| 	AttachVirtualDiskFlagSinglePartition               AttachVirtualDiskFlag = 0x00000100 | ||||
| 	AttachVirtualDiskFlagRegisterVolume                AttachVirtualDiskFlag = 0x00000200 | ||||
| 
 | ||||
| 	// Flags for detaching a VHD
 | ||||
| 	// Flags for detaching a VHD.
 | ||||
| 	DetachVirtualDiskFlagNone DetachVirtualDiskFlag = 0x0 | ||||
| ) | ||||
| 
 | ||||
| // CreateVhdx is a helper function to create a simple vhdx file at the given path using
 | ||||
| // default values.
 | ||||
| //
 | ||||
| //revive:disable-next-line:var-naming VHDX, not Vhdx
 | ||||
| func CreateVhdx(path string, maxSizeInGb, blockSizeInMb uint32) error { | ||||
| 	params := CreateVirtualDiskParameters{ | ||||
| 		Version: 2, | ||||
|  | @ -172,6 +175,8 @@ func DetachVirtualDisk(handle syscall.Handle) (err error) { | |||
| } | ||||
| 
 | ||||
| // DetachVhd detaches a vhd found at `path`.
 | ||||
| //
 | ||||
| //revive:disable-next-line:var-naming VHD, not Vhd
 | ||||
| func DetachVhd(path string) error { | ||||
| 	handle, err := OpenVirtualDisk( | ||||
| 		path, | ||||
|  | @ -181,12 +186,16 @@ func DetachVhd(path string) error { | |||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	defer syscall.CloseHandle(handle) | ||||
| 	defer syscall.CloseHandle(handle) //nolint:errcheck
 | ||||
| 	return DetachVirtualDisk(handle) | ||||
| } | ||||
| 
 | ||||
| // AttachVirtualDisk attaches a virtual hard disk for use.
 | ||||
| func AttachVirtualDisk(handle syscall.Handle, attachVirtualDiskFlag AttachVirtualDiskFlag, parameters *AttachVirtualDiskParameters) (err error) { | ||||
| func AttachVirtualDisk( | ||||
| 	handle syscall.Handle, | ||||
| 	attachVirtualDiskFlag AttachVirtualDiskFlag, | ||||
| 	parameters *AttachVirtualDiskParameters, | ||||
| ) (err error) { | ||||
| 	// Supports both version 1 and 2 of the attach parameters as version 2 wasn't present in RS5.
 | ||||
| 	if err := attachVirtualDisk( | ||||
| 		handle, | ||||
|  | @ -203,6 +212,8 @@ func AttachVirtualDisk(handle syscall.Handle, attachVirtualDiskFlag AttachVirtua | |||
| 
 | ||||
| // AttachVhd attaches a virtual hard disk at `path` for use. Attaches using version 2
 | ||||
| // of the ATTACH_VIRTUAL_DISK_PARAMETERS.
 | ||||
| //
 | ||||
| //revive:disable-next-line:var-naming VHD, not Vhd
 | ||||
| func AttachVhd(path string) (err error) { | ||||
| 	handle, err := OpenVirtualDisk( | ||||
| 		path, | ||||
|  | @ -213,7 +224,7 @@ func AttachVhd(path string) (err error) { | |||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	defer syscall.CloseHandle(handle) | ||||
| 	defer syscall.CloseHandle(handle) //nolint:errcheck
 | ||||
| 	params := AttachVirtualDiskParameters{Version: 2} | ||||
| 	if err := AttachVirtualDisk( | ||||
| 		handle, | ||||
|  | @ -226,7 +237,11 @@ func AttachVhd(path string) (err error) { | |||
| } | ||||
| 
 | ||||
| // OpenVirtualDisk obtains a handle to a VHD opened with supplied access mask and flags.
 | ||||
| func OpenVirtualDisk(vhdPath string, virtualDiskAccessMask VirtualDiskAccessMask, openVirtualDiskFlags VirtualDiskFlag) (syscall.Handle, error) { | ||||
| func OpenVirtualDisk( | ||||
| 	vhdPath string, | ||||
| 	virtualDiskAccessMask VirtualDiskAccessMask, | ||||
| 	openVirtualDiskFlags VirtualDiskFlag, | ||||
| ) (syscall.Handle, error) { | ||||
| 	parameters := OpenVirtualDiskParameters{Version: 2} | ||||
| 	handle, err := OpenVirtualDiskWithParameters( | ||||
| 		vhdPath, | ||||
|  | @ -241,7 +256,12 @@ func OpenVirtualDisk(vhdPath string, virtualDiskAccessMask VirtualDiskAccessMask | |||
| } | ||||
| 
 | ||||
| // OpenVirtualDiskWithParameters obtains a handle to a VHD opened with supplied access mask, flags and parameters.
 | ||||
| func OpenVirtualDiskWithParameters(vhdPath string, virtualDiskAccessMask VirtualDiskAccessMask, openVirtualDiskFlags VirtualDiskFlag, parameters *OpenVirtualDiskParameters) (syscall.Handle, error) { | ||||
| func OpenVirtualDiskWithParameters( | ||||
| 	vhdPath string, | ||||
| 	virtualDiskAccessMask VirtualDiskAccessMask, | ||||
| 	openVirtualDiskFlags VirtualDiskFlag, | ||||
| 	parameters *OpenVirtualDiskParameters, | ||||
| ) (syscall.Handle, error) { | ||||
| 	var ( | ||||
| 		handle      syscall.Handle | ||||
| 		defaultType VirtualStorageType | ||||
|  | @ -279,7 +299,12 @@ func OpenVirtualDiskWithParameters(vhdPath string, virtualDiskAccessMask Virtual | |||
| } | ||||
| 
 | ||||
| // CreateVirtualDisk creates a virtual harddisk and returns a handle to the disk.
 | ||||
| func CreateVirtualDisk(path string, virtualDiskAccessMask VirtualDiskAccessMask, createVirtualDiskFlags CreateVirtualDiskFlag, parameters *CreateVirtualDiskParameters) (syscall.Handle, error) { | ||||
| func CreateVirtualDisk( | ||||
| 	path string, | ||||
| 	virtualDiskAccessMask VirtualDiskAccessMask, | ||||
| 	createVirtualDiskFlags CreateVirtualDiskFlag, | ||||
| 	parameters *CreateVirtualDiskParameters, | ||||
| ) (syscall.Handle, error) { | ||||
| 	var ( | ||||
| 		handle      syscall.Handle | ||||
| 		defaultType VirtualStorageType | ||||
|  | @ -323,6 +348,8 @@ func GetVirtualDiskPhysicalPath(handle syscall.Handle) (_ string, err error) { | |||
| } | ||||
| 
 | ||||
| // CreateDiffVhd is a helper function to create a differencing virtual disk.
 | ||||
| //
 | ||||
| //revive:disable-next-line:var-naming VHD, not Vhd
 | ||||
| func CreateDiffVhd(diffVhdPath, baseVhdPath string, blockSizeInMB uint32) error { | ||||
| 	// Setting `ParentPath` is how to signal to create a differencing disk.
 | ||||
| 	createParams := &CreateVirtualDiskParameters{ | ||||
|  |  | |||
|  | @ -1,4 +1,6 @@ | |||
| // Code generated by 'go generate'; DO NOT EDIT.
 | ||||
| //go:build windows
 | ||||
| 
 | ||||
| // Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT.
 | ||||
| 
 | ||||
| package vhd | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,4 +1,6 @@ | |||
| // Code generated by 'go generate'; DO NOT EDIT.
 | ||||
| //go:build windows
 | ||||
| 
 | ||||
| // Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT.
 | ||||
| 
 | ||||
| package winio | ||||
| 
 | ||||
|  | @ -47,9 +49,11 @@ var ( | |||
| 	procConvertSecurityDescriptorToStringSecurityDescriptorW = modadvapi32.NewProc("ConvertSecurityDescriptorToStringSecurityDescriptorW") | ||||
| 	procConvertSidToStringSidW                               = modadvapi32.NewProc("ConvertSidToStringSidW") | ||||
| 	procConvertStringSecurityDescriptorToSecurityDescriptorW = modadvapi32.NewProc("ConvertStringSecurityDescriptorToSecurityDescriptorW") | ||||
| 	procConvertStringSidToSidW                               = modadvapi32.NewProc("ConvertStringSidToSidW") | ||||
| 	procGetSecurityDescriptorLength                          = modadvapi32.NewProc("GetSecurityDescriptorLength") | ||||
| 	procImpersonateSelf                                      = modadvapi32.NewProc("ImpersonateSelf") | ||||
| 	procLookupAccountNameW                                   = modadvapi32.NewProc("LookupAccountNameW") | ||||
| 	procLookupAccountSidW                                    = modadvapi32.NewProc("LookupAccountSidW") | ||||
| 	procLookupPrivilegeDisplayNameW                          = modadvapi32.NewProc("LookupPrivilegeDisplayNameW") | ||||
| 	procLookupPrivilegeNameW                                 = modadvapi32.NewProc("LookupPrivilegeNameW") | ||||
| 	procLookupPrivilegeValueW                                = modadvapi32.NewProc("LookupPrivilegeValueW") | ||||
|  | @ -74,7 +78,6 @@ var ( | |||
| 	procRtlDosPathNameToNtPathName_U                         = modntdll.NewProc("RtlDosPathNameToNtPathName_U") | ||||
| 	procRtlNtStatusToDosErrorNoTeb                           = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb") | ||||
| 	procWSAGetOverlappedResult                               = modws2_32.NewProc("WSAGetOverlappedResult") | ||||
| 	procbind                                                 = modws2_32.NewProc("bind") | ||||
| ) | ||||
| 
 | ||||
| func adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) { | ||||
|  | @ -123,6 +126,14 @@ func _convertStringSecurityDescriptorToSecurityDescriptor(str *uint16, revision | |||
| 	return | ||||
| } | ||||
| 
 | ||||
| func convertStringSidToSid(str *uint16, sid **byte) (err error) { | ||||
| 	r1, _, e1 := syscall.Syscall(procConvertStringSidToSidW.Addr(), 2, uintptr(unsafe.Pointer(str)), uintptr(unsafe.Pointer(sid)), 0) | ||||
| 	if r1 == 0 { | ||||
| 		err = errnoErr(e1) | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func getSecurityDescriptorLength(sd uintptr) (len uint32) { | ||||
| 	r0, _, _ := syscall.Syscall(procGetSecurityDescriptorLength.Addr(), 1, uintptr(sd), 0, 0) | ||||
| 	len = uint32(r0) | ||||
|  | @ -154,6 +165,14 @@ func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidS | |||
| 	return | ||||
| } | ||||
| 
 | ||||
| func lookupAccountSid(systemName *uint16, sid *byte, name *uint16, nameSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) { | ||||
| 	r1, _, e1 := syscall.Syscall9(procLookupAccountSidW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)), 0, 0) | ||||
| 	if r1 == 0 { | ||||
| 		err = errnoErr(e1) | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) { | ||||
| 	var _p0 *uint16 | ||||
| 	_p0, err = syscall.UTF16PtrFromString(systemName) | ||||
|  | @ -380,25 +399,25 @@ func setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err erro | |||
| 	return | ||||
| } | ||||
| 
 | ||||
| func ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntstatus) { | ||||
| func ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntStatus) { | ||||
| 	r0, _, _ := syscall.Syscall15(procNtCreateNamedPipeFile.Addr(), 14, uintptr(unsafe.Pointer(pipe)), uintptr(access), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(share), uintptr(disposition), uintptr(options), uintptr(typ), uintptr(readMode), uintptr(completionMode), uintptr(maxInstances), uintptr(inboundQuota), uintptr(outputQuota), uintptr(unsafe.Pointer(timeout)), 0) | ||||
| 	status = ntstatus(r0) | ||||
| 	status = ntStatus(r0) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func rtlDefaultNpAcl(dacl *uintptr) (status ntstatus) { | ||||
| func rtlDefaultNpAcl(dacl *uintptr) (status ntStatus) { | ||||
| 	r0, _, _ := syscall.Syscall(procRtlDefaultNpAcl.Addr(), 1, uintptr(unsafe.Pointer(dacl)), 0, 0) | ||||
| 	status = ntstatus(r0) | ||||
| 	status = ntStatus(r0) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntstatus) { | ||||
| func rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntStatus) { | ||||
| 	r0, _, _ := syscall.Syscall6(procRtlDosPathNameToNtPathName_U.Addr(), 4, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(ntName)), uintptr(filePart), uintptr(reserved), 0, 0) | ||||
| 	status = ntstatus(r0) | ||||
| 	status = ntStatus(r0) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func rtlNtStatusToDosError(status ntstatus) (winerr error) { | ||||
| func rtlNtStatusToDosError(status ntStatus) (winerr error) { | ||||
| 	r0, _, _ := syscall.Syscall(procRtlNtStatusToDosErrorNoTeb.Addr(), 1, uintptr(status), 0, 0) | ||||
| 	if r0 != 0 { | ||||
| 		winerr = syscall.Errno(r0) | ||||
|  | @ -417,11 +436,3 @@ func wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint | |||
| 	} | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func bind(s syscall.Handle, name unsafe.Pointer, namelen int32) (err error) { | ||||
| 	r1, _, e1 := syscall.Syscall(procbind.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen)) | ||||
| 	if r1 == socketError { | ||||
| 		err = errnoErr(e1) | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  |  | |||
|  | @ -24,7 +24,7 @@ info: | |||
|   title: "Docker Engine API" | ||||
|   version: "1.41" | ||||
|   x-logo: | ||||
|     url: "https://docs.docker.com/images/logo-docker-main.png" | ||||
|     url: "https://docs.docker.com/assets/images/logo-docker-main.png" | ||||
|   description: | | ||||
|     The Engine API is an HTTP API served by Docker Engine. It is the API the | ||||
|     Docker client uses to communicate with the Engine, so everything the Docker | ||||
|  |  | |||
|  | @ -6,6 +6,7 @@ package docker | |||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"context" | ||||
| 	"encoding/base64" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
|  | @ -262,11 +263,21 @@ type AuthStatus struct { | |||
| //
 | ||||
| // See https://goo.gl/6nsZkH for more details.
 | ||||
| func (c *Client) AuthCheck(conf *AuthConfiguration) (AuthStatus, error) { | ||||
| 	return c.AuthCheckWithContext(conf, context.TODO()) | ||||
| } | ||||
| 
 | ||||
| // AuthCheckWithContext validates the given credentials. It returns nil if successful. The context object
 | ||||
| // can be used to cancel the request.
 | ||||
| //
 | ||||
| // For Docker API versions >= 1.23, the AuthStatus struct will be populated, otherwise it will be empty.
 | ||||
| //
 | ||||
| // See https://goo.gl/6nsZkH for more details.
 | ||||
| func (c *Client) AuthCheckWithContext(conf *AuthConfiguration, ctx context.Context) (AuthStatus, error) { | ||||
| 	var authStatus AuthStatus | ||||
| 	if conf == nil { | ||||
| 		return authStatus, errors.New("conf is nil") | ||||
| 	} | ||||
| 	resp, err := c.do(http.MethodPost, "/auth", doOptions{data: conf}) | ||||
| 	resp, err := c.do(http.MethodPost, "/auth", doOptions{data: conf, context: ctx}) | ||||
| 	if err != nil { | ||||
| 		return authStatus, err | ||||
| 	} | ||||
|  |  | |||
|  | @ -3,7 +3,6 @@ | |||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| //go:build !windows
 | ||||
| // +build !windows
 | ||||
| 
 | ||||
| package docker | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,27 @@ | |||
| Copyright (c) 2009 The Go Authors. All rights reserved. | ||||
| 
 | ||||
| Redistribution and use in source and binary forms, with or without | ||||
| modification, are permitted provided that the following conditions are | ||||
| met: | ||||
| 
 | ||||
|    * Redistributions of source code must retain the above copyright | ||||
| notice, this list of conditions and the following disclaimer. | ||||
|    * Redistributions in binary form must reproduce the above | ||||
| copyright notice, this list of conditions and the following disclaimer | ||||
| in the documentation and/or other materials provided with the | ||||
| distribution. | ||||
|    * Neither the name of Google Inc. nor the names of its | ||||
| contributors may be used to endorse or promote products derived from | ||||
| this software without specific prior written permission. | ||||
| 
 | ||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | @ -0,0 +1,22 @@ | |||
| Additional IP Rights Grant (Patents) | ||||
| 
 | ||||
| "This implementation" means the copyrightable works distributed by | ||||
| Google as part of the Go project. | ||||
| 
 | ||||
| Google 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, | ||||
| transfer and otherwise run, modify and propagate the contents of this | ||||
| implementation of Go, where such license applies only to those patent | ||||
| claims, both currently owned or controlled by Google and acquired in | ||||
| the future, licensable by Google that are necessarily infringed by this | ||||
| implementation of Go.  This grant does not include claims that would be | ||||
| infringed only as a consequence of further modification of this | ||||
| implementation.  If you or your agent or exclusive licensee institute or | ||||
| order or agree to the institution of patent litigation against any | ||||
| entity (including a cross-claim or counterclaim in a lawsuit) alleging | ||||
| that this implementation of Go or any code incorporated within this | ||||
| implementation of Go constitutes direct or contributory patent | ||||
| infringement, or inducement of patent infringement, then any patent | ||||
| rights granted to you under this License for this implementation of Go | ||||
| shall terminate as of the date such litigation is filed. | ||||
|  | @ -0,0 +1,401 @@ | |||
| // Copyright 2018 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.
 | ||||
| 
 | ||||
| // Package semver implements comparison of semantic version strings.
 | ||||
| // In this package, semantic version strings must begin with a leading "v",
 | ||||
| // as in "v1.0.0".
 | ||||
| //
 | ||||
| // The general form of a semantic version string accepted by this package is
 | ||||
| //
 | ||||
| //	vMAJOR[.MINOR[.PATCH[-PRERELEASE][+BUILD]]]
 | ||||
| //
 | ||||
| // where square brackets indicate optional parts of the syntax;
 | ||||
| // MAJOR, MINOR, and PATCH are decimal integers without extra leading zeros;
 | ||||
| // PRERELEASE and BUILD are each a series of non-empty dot-separated identifiers
 | ||||
| // using only alphanumeric characters and hyphens; and
 | ||||
| // all-numeric PRERELEASE identifiers must not have leading zeros.
 | ||||
| //
 | ||||
| // This package follows Semantic Versioning 2.0.0 (see semver.org)
 | ||||
| // with two exceptions. First, it requires the "v" prefix. Second, it recognizes
 | ||||
| // vMAJOR and vMAJOR.MINOR (with no prerelease or build suffixes)
 | ||||
| // as shorthands for vMAJOR.0.0 and vMAJOR.MINOR.0.
 | ||||
| package semver | ||||
| 
 | ||||
| import "sort" | ||||
| 
 | ||||
| // parsed returns the parsed form of a semantic version string.
 | ||||
| type parsed struct { | ||||
| 	major      string | ||||
| 	minor      string | ||||
| 	patch      string | ||||
| 	short      string | ||||
| 	prerelease string | ||||
| 	build      string | ||||
| } | ||||
| 
 | ||||
| // IsValid reports whether v is a valid semantic version string.
 | ||||
| func IsValid(v string) bool { | ||||
| 	_, ok := parse(v) | ||||
| 	return ok | ||||
| } | ||||
| 
 | ||||
| // Canonical returns the canonical formatting of the semantic version v.
 | ||||
| // It fills in any missing .MINOR or .PATCH and discards build metadata.
 | ||||
| // Two semantic versions compare equal only if their canonical formattings
 | ||||
| // are identical strings.
 | ||||
| // The canonical invalid semantic version is the empty string.
 | ||||
| func Canonical(v string) string { | ||||
| 	p, ok := parse(v) | ||||
| 	if !ok { | ||||
| 		return "" | ||||
| 	} | ||||
| 	if p.build != "" { | ||||
| 		return v[:len(v)-len(p.build)] | ||||
| 	} | ||||
| 	if p.short != "" { | ||||
| 		return v + p.short | ||||
| 	} | ||||
| 	return v | ||||
| } | ||||
| 
 | ||||
| // Major returns the major version prefix of the semantic version v.
 | ||||
| // For example, Major("v2.1.0") == "v2".
 | ||||
| // If v is an invalid semantic version string, Major returns the empty string.
 | ||||
| func Major(v string) string { | ||||
| 	pv, ok := parse(v) | ||||
| 	if !ok { | ||||
| 		return "" | ||||
| 	} | ||||
| 	return v[:1+len(pv.major)] | ||||
| } | ||||
| 
 | ||||
| // MajorMinor returns the major.minor version prefix of the semantic version v.
 | ||||
| // For example, MajorMinor("v2.1.0") == "v2.1".
 | ||||
| // If v is an invalid semantic version string, MajorMinor returns the empty string.
 | ||||
| func MajorMinor(v string) string { | ||||
| 	pv, ok := parse(v) | ||||
| 	if !ok { | ||||
| 		return "" | ||||
| 	} | ||||
| 	i := 1 + len(pv.major) | ||||
| 	if j := i + 1 + len(pv.minor); j <= len(v) && v[i] == '.' && v[i+1:j] == pv.minor { | ||||
| 		return v[:j] | ||||
| 	} | ||||
| 	return v[:i] + "." + pv.minor | ||||
| } | ||||
| 
 | ||||
| // Prerelease returns the prerelease suffix of the semantic version v.
 | ||||
| // For example, Prerelease("v2.1.0-pre+meta") == "-pre".
 | ||||
| // If v is an invalid semantic version string, Prerelease returns the empty string.
 | ||||
| func Prerelease(v string) string { | ||||
| 	pv, ok := parse(v) | ||||
| 	if !ok { | ||||
| 		return "" | ||||
| 	} | ||||
| 	return pv.prerelease | ||||
| } | ||||
| 
 | ||||
| // Build returns the build suffix of the semantic version v.
 | ||||
| // For example, Build("v2.1.0+meta") == "+meta".
 | ||||
| // If v is an invalid semantic version string, Build returns the empty string.
 | ||||
| func Build(v string) string { | ||||
| 	pv, ok := parse(v) | ||||
| 	if !ok { | ||||
| 		return "" | ||||
| 	} | ||||
| 	return pv.build | ||||
| } | ||||
| 
 | ||||
| // Compare returns an integer comparing two versions according to
 | ||||
| // semantic version precedence.
 | ||||
| // The result will be 0 if v == w, -1 if v < w, or +1 if v > w.
 | ||||
| //
 | ||||
| // An invalid semantic version string is considered less than a valid one.
 | ||||
| // All invalid semantic version strings compare equal to each other.
 | ||||
| func Compare(v, w string) int { | ||||
| 	pv, ok1 := parse(v) | ||||
| 	pw, ok2 := parse(w) | ||||
| 	if !ok1 && !ok2 { | ||||
| 		return 0 | ||||
| 	} | ||||
| 	if !ok1 { | ||||
| 		return -1 | ||||
| 	} | ||||
| 	if !ok2 { | ||||
| 		return +1 | ||||
| 	} | ||||
| 	if c := compareInt(pv.major, pw.major); c != 0 { | ||||
| 		return c | ||||
| 	} | ||||
| 	if c := compareInt(pv.minor, pw.minor); c != 0 { | ||||
| 		return c | ||||
| 	} | ||||
| 	if c := compareInt(pv.patch, pw.patch); c != 0 { | ||||
| 		return c | ||||
| 	} | ||||
| 	return comparePrerelease(pv.prerelease, pw.prerelease) | ||||
| } | ||||
| 
 | ||||
| // Max canonicalizes its arguments and then returns the version string
 | ||||
| // that compares greater.
 | ||||
| //
 | ||||
| // Deprecated: use Compare instead. In most cases, returning a canonicalized
 | ||||
| // version is not expected or desired.
 | ||||
| func Max(v, w string) string { | ||||
| 	v = Canonical(v) | ||||
| 	w = Canonical(w) | ||||
| 	if Compare(v, w) > 0 { | ||||
| 		return v | ||||
| 	} | ||||
| 	return w | ||||
| } | ||||
| 
 | ||||
| // ByVersion implements sort.Interface for sorting semantic version strings.
 | ||||
| type ByVersion []string | ||||
| 
 | ||||
| func (vs ByVersion) Len() int      { return len(vs) } | ||||
| func (vs ByVersion) Swap(i, j int) { vs[i], vs[j] = vs[j], vs[i] } | ||||
| func (vs ByVersion) Less(i, j int) bool { | ||||
| 	cmp := Compare(vs[i], vs[j]) | ||||
| 	if cmp != 0 { | ||||
| 		return cmp < 0 | ||||
| 	} | ||||
| 	return vs[i] < vs[j] | ||||
| } | ||||
| 
 | ||||
| // Sort sorts a list of semantic version strings using ByVersion.
 | ||||
| func Sort(list []string) { | ||||
| 	sort.Sort(ByVersion(list)) | ||||
| } | ||||
| 
 | ||||
| func parse(v string) (p parsed, ok bool) { | ||||
| 	if v == "" || v[0] != 'v' { | ||||
| 		return | ||||
| 	} | ||||
| 	p.major, v, ok = parseInt(v[1:]) | ||||
| 	if !ok { | ||||
| 		return | ||||
| 	} | ||||
| 	if v == "" { | ||||
| 		p.minor = "0" | ||||
| 		p.patch = "0" | ||||
| 		p.short = ".0.0" | ||||
| 		return | ||||
| 	} | ||||
| 	if v[0] != '.' { | ||||
| 		ok = false | ||||
| 		return | ||||
| 	} | ||||
| 	p.minor, v, ok = parseInt(v[1:]) | ||||
| 	if !ok { | ||||
| 		return | ||||
| 	} | ||||
| 	if v == "" { | ||||
| 		p.patch = "0" | ||||
| 		p.short = ".0" | ||||
| 		return | ||||
| 	} | ||||
| 	if v[0] != '.' { | ||||
| 		ok = false | ||||
| 		return | ||||
| 	} | ||||
| 	p.patch, v, ok = parseInt(v[1:]) | ||||
| 	if !ok { | ||||
| 		return | ||||
| 	} | ||||
| 	if len(v) > 0 && v[0] == '-' { | ||||
| 		p.prerelease, v, ok = parsePrerelease(v) | ||||
| 		if !ok { | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| 	if len(v) > 0 && v[0] == '+' { | ||||
| 		p.build, v, ok = parseBuild(v) | ||||
| 		if !ok { | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| 	if v != "" { | ||||
| 		ok = false | ||||
| 		return | ||||
| 	} | ||||
| 	ok = true | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func parseInt(v string) (t, rest string, ok bool) { | ||||
| 	if v == "" { | ||||
| 		return | ||||
| 	} | ||||
| 	if v[0] < '0' || '9' < v[0] { | ||||
| 		return | ||||
| 	} | ||||
| 	i := 1 | ||||
| 	for i < len(v) && '0' <= v[i] && v[i] <= '9' { | ||||
| 		i++ | ||||
| 	} | ||||
| 	if v[0] == '0' && i != 1 { | ||||
| 		return | ||||
| 	} | ||||
| 	return v[:i], v[i:], true | ||||
| } | ||||
| 
 | ||||
| func parsePrerelease(v string) (t, rest string, ok bool) { | ||||
| 	// "A pre-release version MAY be denoted by appending a hyphen and
 | ||||
| 	// a series of dot separated identifiers immediately following the patch version.
 | ||||
| 	// Identifiers MUST comprise only ASCII alphanumerics and hyphen [0-9A-Za-z-].
 | ||||
| 	// Identifiers MUST NOT be empty. Numeric identifiers MUST NOT include leading zeroes."
 | ||||
| 	if v == "" || v[0] != '-' { | ||||
| 		return | ||||
| 	} | ||||
| 	i := 1 | ||||
| 	start := 1 | ||||
| 	for i < len(v) && v[i] != '+' { | ||||
| 		if !isIdentChar(v[i]) && v[i] != '.' { | ||||
| 			return | ||||
| 		} | ||||
| 		if v[i] == '.' { | ||||
| 			if start == i || isBadNum(v[start:i]) { | ||||
| 				return | ||||
| 			} | ||||
| 			start = i + 1 | ||||
| 		} | ||||
| 		i++ | ||||
| 	} | ||||
| 	if start == i || isBadNum(v[start:i]) { | ||||
| 		return | ||||
| 	} | ||||
| 	return v[:i], v[i:], true | ||||
| } | ||||
| 
 | ||||
| func parseBuild(v string) (t, rest string, ok bool) { | ||||
| 	if v == "" || v[0] != '+' { | ||||
| 		return | ||||
| 	} | ||||
| 	i := 1 | ||||
| 	start := 1 | ||||
| 	for i < len(v) { | ||||
| 		if !isIdentChar(v[i]) && v[i] != '.' { | ||||
| 			return | ||||
| 		} | ||||
| 		if v[i] == '.' { | ||||
| 			if start == i { | ||||
| 				return | ||||
| 			} | ||||
| 			start = i + 1 | ||||
| 		} | ||||
| 		i++ | ||||
| 	} | ||||
| 	if start == i { | ||||
| 		return | ||||
| 	} | ||||
| 	return v[:i], v[i:], true | ||||
| } | ||||
| 
 | ||||
| func isIdentChar(c byte) bool { | ||||
| 	return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '-' | ||||
| } | ||||
| 
 | ||||
| func isBadNum(v string) bool { | ||||
| 	i := 0 | ||||
| 	for i < len(v) && '0' <= v[i] && v[i] <= '9' { | ||||
| 		i++ | ||||
| 	} | ||||
| 	return i == len(v) && i > 1 && v[0] == '0' | ||||
| } | ||||
| 
 | ||||
| func isNum(v string) bool { | ||||
| 	i := 0 | ||||
| 	for i < len(v) && '0' <= v[i] && v[i] <= '9' { | ||||
| 		i++ | ||||
| 	} | ||||
| 	return i == len(v) | ||||
| } | ||||
| 
 | ||||
| func compareInt(x, y string) int { | ||||
| 	if x == y { | ||||
| 		return 0 | ||||
| 	} | ||||
| 	if len(x) < len(y) { | ||||
| 		return -1 | ||||
| 	} | ||||
| 	if len(x) > len(y) { | ||||
| 		return +1 | ||||
| 	} | ||||
| 	if x < y { | ||||
| 		return -1 | ||||
| 	} else { | ||||
| 		return +1 | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func comparePrerelease(x, y string) int { | ||||
| 	// "When major, minor, and patch are equal, a pre-release version has
 | ||||
| 	// lower precedence than a normal version.
 | ||||
| 	// Example: 1.0.0-alpha < 1.0.0.
 | ||||
| 	// Precedence for two pre-release versions with the same major, minor,
 | ||||
| 	// and patch version MUST be determined by comparing each dot separated
 | ||||
| 	// identifier from left to right until a difference is found as follows:
 | ||||
| 	// identifiers consisting of only digits are compared numerically and
 | ||||
| 	// identifiers with letters or hyphens are compared lexically in ASCII
 | ||||
| 	// sort order. Numeric identifiers always have lower precedence than
 | ||||
| 	// non-numeric identifiers. A larger set of pre-release fields has a
 | ||||
| 	// higher precedence than a smaller set, if all of the preceding
 | ||||
| 	// identifiers are equal.
 | ||||
| 	// Example: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta <
 | ||||
| 	// 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0."
 | ||||
| 	if x == y { | ||||
| 		return 0 | ||||
| 	} | ||||
| 	if x == "" { | ||||
| 		return +1 | ||||
| 	} | ||||
| 	if y == "" { | ||||
| 		return -1 | ||||
| 	} | ||||
| 	for x != "" && y != "" { | ||||
| 		x = x[1:] // skip - or .
 | ||||
| 		y = y[1:] // skip - or .
 | ||||
| 		var dx, dy string | ||||
| 		dx, x = nextIdent(x) | ||||
| 		dy, y = nextIdent(y) | ||||
| 		if dx != dy { | ||||
| 			ix := isNum(dx) | ||||
| 			iy := isNum(dy) | ||||
| 			if ix != iy { | ||||
| 				if ix { | ||||
| 					return -1 | ||||
| 				} else { | ||||
| 					return +1 | ||||
| 				} | ||||
| 			} | ||||
| 			if ix { | ||||
| 				if len(dx) < len(dy) { | ||||
| 					return -1 | ||||
| 				} | ||||
| 				if len(dx) > len(dy) { | ||||
| 					return +1 | ||||
| 				} | ||||
| 			} | ||||
| 			if dx < dy { | ||||
| 				return -1 | ||||
| 			} else { | ||||
| 				return +1 | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	if x == "" { | ||||
| 		return -1 | ||||
| 	} else { | ||||
| 		return +1 | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func nextIdent(x string) (dx, rest string) { | ||||
| 	i := 0 | ||||
| 	for i < len(x) && x[i] != '.' { | ||||
| 		i++ | ||||
| 	} | ||||
| 	return x[:i], x[i:] | ||||
| } | ||||
|  | @ -0,0 +1,27 @@ | |||
| Copyright (c) 2009 The Go Authors. All rights reserved. | ||||
| 
 | ||||
| Redistribution and use in source and binary forms, with or without | ||||
| modification, are permitted provided that the following conditions are | ||||
| met: | ||||
| 
 | ||||
|    * Redistributions of source code must retain the above copyright | ||||
| notice, this list of conditions and the following disclaimer. | ||||
|    * Redistributions in binary form must reproduce the above | ||||
| copyright notice, this list of conditions and the following disclaimer | ||||
| in the documentation and/or other materials provided with the | ||||
| distribution. | ||||
|    * Neither the name of Google Inc. nor the names of its | ||||
| contributors may be used to endorse or promote products derived from | ||||
| this software without specific prior written permission. | ||||
| 
 | ||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | @ -0,0 +1,22 @@ | |||
| Additional IP Rights Grant (Patents) | ||||
| 
 | ||||
| "This implementation" means the copyrightable works distributed by | ||||
| Google as part of the Go project. | ||||
| 
 | ||||
| Google 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, | ||||
| transfer and otherwise run, modify and propagate the contents of this | ||||
| implementation of Go, where such license applies only to those patent | ||||
| claims, both currently owned or controlled by Google and acquired in | ||||
| the future, licensable by Google that are necessarily infringed by this | ||||
| implementation of Go.  This grant does not include claims that would be | ||||
| infringed only as a consequence of further modification of this | ||||
| implementation.  If you or your agent or exclusive licensee institute or | ||||
| order or agree to the institution of patent litigation against any | ||||
| entity (including a cross-claim or counterclaim in a lawsuit) alleging | ||||
| that this implementation of Go or any code incorporated within this | ||||
| implementation of Go constitutes direct or contributory patent | ||||
| infringement, or inducement of patent infringement, then any patent | ||||
| rights granted to you under this License for this implementation of Go | ||||
| shall terminate as of the date such litigation is filed. | ||||
|  | @ -0,0 +1,658 @@ | |||
| // Copyright 2014 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.
 | ||||
| 
 | ||||
| // Stringer is a tool to automate the creation of methods that satisfy the fmt.Stringer
 | ||||
| // interface. Given the name of a (signed or unsigned) integer type T that has constants
 | ||||
| // defined, stringer will create a new self-contained Go source file implementing
 | ||||
| //
 | ||||
| //	func (t T) String() string
 | ||||
| //
 | ||||
| // The file is created in the same package and directory as the package that defines T.
 | ||||
| // It has helpful defaults designed for use with go generate.
 | ||||
| //
 | ||||
| // Stringer works best with constants that are consecutive values such as created using iota,
 | ||||
| // but creates good code regardless. In the future it might also provide custom support for
 | ||||
| // constant sets that are bit patterns.
 | ||||
| //
 | ||||
| // For example, given this snippet,
 | ||||
| //
 | ||||
| //	package painkiller
 | ||||
| //
 | ||||
| //	type Pill int
 | ||||
| //
 | ||||
| //	const (
 | ||||
| //		Placebo Pill = iota
 | ||||
| //		Aspirin
 | ||||
| //		Ibuprofen
 | ||||
| //		Paracetamol
 | ||||
| //		Acetaminophen = Paracetamol
 | ||||
| //	)
 | ||||
| //
 | ||||
| // running this command
 | ||||
| //
 | ||||
| //	stringer -type=Pill
 | ||||
| //
 | ||||
| // in the same directory will create the file pill_string.go, in package painkiller,
 | ||||
| // containing a definition of
 | ||||
| //
 | ||||
| //	func (Pill) String() string
 | ||||
| //
 | ||||
| // That method will translate the value of a Pill constant to the string representation
 | ||||
| // of the respective constant name, so that the call fmt.Print(painkiller.Aspirin) will
 | ||||
| // print the string "Aspirin".
 | ||||
| //
 | ||||
| // Typically this process would be run using go generate, like this:
 | ||||
| //
 | ||||
| //	//go:generate stringer -type=Pill
 | ||||
| //
 | ||||
| // If multiple constants have the same value, the lexically first matching name will
 | ||||
| // be used (in the example, Acetaminophen will print as "Paracetamol").
 | ||||
| //
 | ||||
| // With no arguments, it processes the package in the current directory.
 | ||||
| // Otherwise, the arguments must name a single directory holding a Go package
 | ||||
| // or a set of Go source files that represent a single Go package.
 | ||||
| //
 | ||||
| // The -type flag accepts a comma-separated list of types so a single run can
 | ||||
| // generate methods for multiple types. The default output file is t_string.go,
 | ||||
| // where t is the lower-cased name of the first type listed. It can be overridden
 | ||||
| // with the -output flag.
 | ||||
| //
 | ||||
| // The -linecomment flag tells stringer to generate the text of any line comment, trimmed
 | ||||
| // of leading spaces, instead of the constant name. For instance, if the constants above had a
 | ||||
| // Pill prefix, one could write
 | ||||
| //
 | ||||
| //	PillAspirin // Aspirin
 | ||||
| //
 | ||||
| // to suppress it in the output.
 | ||||
| package main // import "golang.org/x/tools/cmd/stringer"
 | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"flag" | ||||
| 	"fmt" | ||||
| 	"go/ast" | ||||
| 	"go/constant" | ||||
| 	"go/format" | ||||
| 	"go/token" | ||||
| 	"go/types" | ||||
| 	"io/ioutil" | ||||
| 	"log" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"sort" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"golang.org/x/tools/go/packages" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	typeNames   = flag.String("type", "", "comma-separated list of type names; must be set") | ||||
| 	output      = flag.String("output", "", "output file name; default srcdir/<type>_string.go") | ||||
| 	trimprefix  = flag.String("trimprefix", "", "trim the `prefix` from the generated constant names") | ||||
| 	linecomment = flag.Bool("linecomment", false, "use line comment text as printed text when present") | ||||
| 	buildTags   = flag.String("tags", "", "comma-separated list of build tags to apply") | ||||
| ) | ||||
| 
 | ||||
| // Usage is a replacement usage function for the flags package.
 | ||||
| func Usage() { | ||||
| 	fmt.Fprintf(os.Stderr, "Usage of stringer:\n") | ||||
| 	fmt.Fprintf(os.Stderr, "\tstringer [flags] -type T [directory]\n") | ||||
| 	fmt.Fprintf(os.Stderr, "\tstringer [flags] -type T files... # Must be a single package\n") | ||||
| 	fmt.Fprintf(os.Stderr, "For more information, see:\n") | ||||
| 	fmt.Fprintf(os.Stderr, "\thttps://pkg.go.dev/golang.org/x/tools/cmd/stringer\n") | ||||
| 	fmt.Fprintf(os.Stderr, "Flags:\n") | ||||
| 	flag.PrintDefaults() | ||||
| } | ||||
| 
 | ||||
| func main() { | ||||
| 	log.SetFlags(0) | ||||
| 	log.SetPrefix("stringer: ") | ||||
| 	flag.Usage = Usage | ||||
| 	flag.Parse() | ||||
| 	if len(*typeNames) == 0 { | ||||
| 		flag.Usage() | ||||
| 		os.Exit(2) | ||||
| 	} | ||||
| 	types := strings.Split(*typeNames, ",") | ||||
| 	var tags []string | ||||
| 	if len(*buildTags) > 0 { | ||||
| 		tags = strings.Split(*buildTags, ",") | ||||
| 	} | ||||
| 
 | ||||
| 	// We accept either one directory or a list of files. Which do we have?
 | ||||
| 	args := flag.Args() | ||||
| 	if len(args) == 0 { | ||||
| 		// Default: process whole package in current directory.
 | ||||
| 		args = []string{"."} | ||||
| 	} | ||||
| 
 | ||||
| 	// Parse the package once.
 | ||||
| 	var dir string | ||||
| 	g := Generator{ | ||||
| 		trimPrefix:  *trimprefix, | ||||
| 		lineComment: *linecomment, | ||||
| 	} | ||||
| 	// TODO(suzmue): accept other patterns for packages (directories, list of files, import paths, etc).
 | ||||
| 	if len(args) == 1 && isDirectory(args[0]) { | ||||
| 		dir = args[0] | ||||
| 	} else { | ||||
| 		if len(tags) != 0 { | ||||
| 			log.Fatal("-tags option applies only to directories, not when files are specified") | ||||
| 		} | ||||
| 		dir = filepath.Dir(args[0]) | ||||
| 	} | ||||
| 
 | ||||
| 	g.parsePackage(args, tags) | ||||
| 
 | ||||
| 	// Print the header and package clause.
 | ||||
| 	g.Printf("// Code generated by \"stringer %s\"; DO NOT EDIT.\n", strings.Join(os.Args[1:], " ")) | ||||
| 	g.Printf("\n") | ||||
| 	g.Printf("package %s", g.pkg.name) | ||||
| 	g.Printf("\n") | ||||
| 	g.Printf("import \"strconv\"\n") // Used by all methods.
 | ||||
| 
 | ||||
| 	// Run generate for each type.
 | ||||
| 	for _, typeName := range types { | ||||
| 		g.generate(typeName) | ||||
| 	} | ||||
| 
 | ||||
| 	// Format the output.
 | ||||
| 	src := g.format() | ||||
| 
 | ||||
| 	// Write to file.
 | ||||
| 	outputName := *output | ||||
| 	if outputName == "" { | ||||
| 		baseName := fmt.Sprintf("%s_string.go", types[0]) | ||||
| 		outputName = filepath.Join(dir, strings.ToLower(baseName)) | ||||
| 	} | ||||
| 	err := ioutil.WriteFile(outputName, src, 0644) | ||||
| 	if err != nil { | ||||
| 		log.Fatalf("writing output: %s", err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // isDirectory reports whether the named file is a directory.
 | ||||
| func isDirectory(name string) bool { | ||||
| 	info, err := os.Stat(name) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 	return info.IsDir() | ||||
| } | ||||
| 
 | ||||
| // Generator holds the state of the analysis. Primarily used to buffer
 | ||||
| // the output for format.Source.
 | ||||
| type Generator struct { | ||||
| 	buf bytes.Buffer // Accumulated output.
 | ||||
| 	pkg *Package     // Package we are scanning.
 | ||||
| 
 | ||||
| 	trimPrefix  string | ||||
| 	lineComment bool | ||||
| } | ||||
| 
 | ||||
| func (g *Generator) Printf(format string, args ...interface{}) { | ||||
| 	fmt.Fprintf(&g.buf, format, args...) | ||||
| } | ||||
| 
 | ||||
| // File holds a single parsed file and associated data.
 | ||||
| type File struct { | ||||
| 	pkg  *Package  // Package to which this file belongs.
 | ||||
| 	file *ast.File // Parsed AST.
 | ||||
| 	// These fields are reset for each type being generated.
 | ||||
| 	typeName string  // Name of the constant type.
 | ||||
| 	values   []Value // Accumulator for constant values of that type.
 | ||||
| 
 | ||||
| 	trimPrefix  string | ||||
| 	lineComment bool | ||||
| } | ||||
| 
 | ||||
| type Package struct { | ||||
| 	name  string | ||||
| 	defs  map[*ast.Ident]types.Object | ||||
| 	files []*File | ||||
| } | ||||
| 
 | ||||
| // parsePackage analyzes the single package constructed from the patterns and tags.
 | ||||
| // parsePackage exits if there is an error.
 | ||||
| func (g *Generator) parsePackage(patterns []string, tags []string) { | ||||
| 	cfg := &packages.Config{ | ||||
| 		Mode: packages.NeedName | packages.NeedTypes | packages.NeedTypesInfo | packages.NeedSyntax, | ||||
| 		// TODO: Need to think about constants in test files. Maybe write type_string_test.go
 | ||||
| 		// in a separate pass? For later.
 | ||||
| 		Tests:      false, | ||||
| 		BuildFlags: []string{fmt.Sprintf("-tags=%s", strings.Join(tags, " "))}, | ||||
| 	} | ||||
| 	pkgs, err := packages.Load(cfg, patterns...) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 	if len(pkgs) != 1 { | ||||
| 		log.Fatalf("error: %d packages found", len(pkgs)) | ||||
| 	} | ||||
| 	g.addPackage(pkgs[0]) | ||||
| } | ||||
| 
 | ||||
| // addPackage adds a type checked Package and its syntax files to the generator.
 | ||||
| func (g *Generator) addPackage(pkg *packages.Package) { | ||||
| 	g.pkg = &Package{ | ||||
| 		name:  pkg.Name, | ||||
| 		defs:  pkg.TypesInfo.Defs, | ||||
| 		files: make([]*File, len(pkg.Syntax)), | ||||
| 	} | ||||
| 
 | ||||
| 	for i, file := range pkg.Syntax { | ||||
| 		g.pkg.files[i] = &File{ | ||||
| 			file:        file, | ||||
| 			pkg:         g.pkg, | ||||
| 			trimPrefix:  g.trimPrefix, | ||||
| 			lineComment: g.lineComment, | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // generate produces the String method for the named type.
 | ||||
| func (g *Generator) generate(typeName string) { | ||||
| 	values := make([]Value, 0, 100) | ||||
| 	for _, file := range g.pkg.files { | ||||
| 		// Set the state for this run of the walker.
 | ||||
| 		file.typeName = typeName | ||||
| 		file.values = nil | ||||
| 		if file.file != nil { | ||||
| 			ast.Inspect(file.file, file.genDecl) | ||||
| 			values = append(values, file.values...) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if len(values) == 0 { | ||||
| 		log.Fatalf("no values defined for type %s", typeName) | ||||
| 	} | ||||
| 	// Generate code that will fail if the constants change value.
 | ||||
| 	g.Printf("func _() {\n") | ||||
| 	g.Printf("\t// An \"invalid array index\" compiler error signifies that the constant values have changed.\n") | ||||
| 	g.Printf("\t// Re-run the stringer command to generate them again.\n") | ||||
| 	g.Printf("\tvar x [1]struct{}\n") | ||||
| 	for _, v := range values { | ||||
| 		g.Printf("\t_ = x[%s - %s]\n", v.originalName, v.str) | ||||
| 	} | ||||
| 	g.Printf("}\n") | ||||
| 	runs := splitIntoRuns(values) | ||||
| 	// The decision of which pattern to use depends on the number of
 | ||||
| 	// runs in the numbers. If there's only one, it's easy. For more than
 | ||||
| 	// one, there's a tradeoff between complexity and size of the data
 | ||||
| 	// and code vs. the simplicity of a map. A map takes more space,
 | ||||
| 	// but so does the code. The decision here (crossover at 10) is
 | ||||
| 	// arbitrary, but considers that for large numbers of runs the cost
 | ||||
| 	// of the linear scan in the switch might become important, and
 | ||||
| 	// rather than use yet another algorithm such as binary search,
 | ||||
| 	// we punt and use a map. In any case, the likelihood of a map
 | ||||
| 	// being necessary for any realistic example other than bitmasks
 | ||||
| 	// is very low. And bitmasks probably deserve their own analysis,
 | ||||
| 	// to be done some other day.
 | ||||
| 	switch { | ||||
| 	case len(runs) == 1: | ||||
| 		g.buildOneRun(runs, typeName) | ||||
| 	case len(runs) <= 10: | ||||
| 		g.buildMultipleRuns(runs, typeName) | ||||
| 	default: | ||||
| 		g.buildMap(runs, typeName) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // splitIntoRuns breaks the values into runs of contiguous sequences.
 | ||||
| // For example, given 1,2,3,5,6,7 it returns {1,2,3},{5,6,7}.
 | ||||
| // The input slice is known to be non-empty.
 | ||||
| func splitIntoRuns(values []Value) [][]Value { | ||||
| 	// We use stable sort so the lexically first name is chosen for equal elements.
 | ||||
| 	sort.Stable(byValue(values)) | ||||
| 	// Remove duplicates. Stable sort has put the one we want to print first,
 | ||||
| 	// so use that one. The String method won't care about which named constant
 | ||||
| 	// was the argument, so the first name for the given value is the only one to keep.
 | ||||
| 	// We need to do this because identical values would cause the switch or map
 | ||||
| 	// to fail to compile.
 | ||||
| 	j := 1 | ||||
| 	for i := 1; i < len(values); i++ { | ||||
| 		if values[i].value != values[i-1].value { | ||||
| 			values[j] = values[i] | ||||
| 			j++ | ||||
| 		} | ||||
| 	} | ||||
| 	values = values[:j] | ||||
| 	runs := make([][]Value, 0, 10) | ||||
| 	for len(values) > 0 { | ||||
| 		// One contiguous sequence per outer loop.
 | ||||
| 		i := 1 | ||||
| 		for i < len(values) && values[i].value == values[i-1].value+1 { | ||||
| 			i++ | ||||
| 		} | ||||
| 		runs = append(runs, values[:i]) | ||||
| 		values = values[i:] | ||||
| 	} | ||||
| 	return runs | ||||
| } | ||||
| 
 | ||||
| // format returns the gofmt-ed contents of the Generator's buffer.
 | ||||
| func (g *Generator) format() []byte { | ||||
| 	src, err := format.Source(g.buf.Bytes()) | ||||
| 	if err != nil { | ||||
| 		// Should never happen, but can arise when developing this code.
 | ||||
| 		// The user can compile the output to see the error.
 | ||||
| 		log.Printf("warning: internal error: invalid Go generated: %s", err) | ||||
| 		log.Printf("warning: compile the package to analyze the error") | ||||
| 		return g.buf.Bytes() | ||||
| 	} | ||||
| 	return src | ||||
| } | ||||
| 
 | ||||
| // Value represents a declared constant.
 | ||||
| type Value struct { | ||||
| 	originalName string // The name of the constant.
 | ||||
| 	name         string // The name with trimmed prefix.
 | ||||
| 	// The value is stored as a bit pattern alone. The boolean tells us
 | ||||
| 	// whether to interpret it as an int64 or a uint64; the only place
 | ||||
| 	// this matters is when sorting.
 | ||||
| 	// Much of the time the str field is all we need; it is printed
 | ||||
| 	// by Value.String.
 | ||||
| 	value  uint64 // Will be converted to int64 when needed.
 | ||||
| 	signed bool   // Whether the constant is a signed type.
 | ||||
| 	str    string // The string representation given by the "go/constant" package.
 | ||||
| } | ||||
| 
 | ||||
| func (v *Value) String() string { | ||||
| 	return v.str | ||||
| } | ||||
| 
 | ||||
| // byValue lets us sort the constants into increasing order.
 | ||||
| // We take care in the Less method to sort in signed or unsigned order,
 | ||||
| // as appropriate.
 | ||||
| type byValue []Value | ||||
| 
 | ||||
| func (b byValue) Len() int      { return len(b) } | ||||
| func (b byValue) Swap(i, j int) { b[i], b[j] = b[j], b[i] } | ||||
| func (b byValue) Less(i, j int) bool { | ||||
| 	if b[i].signed { | ||||
| 		return int64(b[i].value) < int64(b[j].value) | ||||
| 	} | ||||
| 	return b[i].value < b[j].value | ||||
| } | ||||
| 
 | ||||
| // genDecl processes one declaration clause.
 | ||||
| func (f *File) genDecl(node ast.Node) bool { | ||||
| 	decl, ok := node.(*ast.GenDecl) | ||||
| 	if !ok || decl.Tok != token.CONST { | ||||
| 		// We only care about const declarations.
 | ||||
| 		return true | ||||
| 	} | ||||
| 	// The name of the type of the constants we are declaring.
 | ||||
| 	// Can change if this is a multi-element declaration.
 | ||||
| 	typ := "" | ||||
| 	// Loop over the elements of the declaration. Each element is a ValueSpec:
 | ||||
| 	// a list of names possibly followed by a type, possibly followed by values.
 | ||||
| 	// If the type and value are both missing, we carry down the type (and value,
 | ||||
| 	// but the "go/types" package takes care of that).
 | ||||
| 	for _, spec := range decl.Specs { | ||||
| 		vspec := spec.(*ast.ValueSpec) // Guaranteed to succeed as this is CONST.
 | ||||
| 		if vspec.Type == nil && len(vspec.Values) > 0 { | ||||
| 			// "X = 1". With no type but a value. If the constant is untyped,
 | ||||
| 			// skip this vspec and reset the remembered type.
 | ||||
| 			typ = "" | ||||
| 
 | ||||
| 			// If this is a simple type conversion, remember the type.
 | ||||
| 			// We don't mind if this is actually a call; a qualified call won't
 | ||||
| 			// be matched (that will be SelectorExpr, not Ident), and only unusual
 | ||||
| 			// situations will result in a function call that appears to be
 | ||||
| 			// a type conversion.
 | ||||
| 			ce, ok := vspec.Values[0].(*ast.CallExpr) | ||||
| 			if !ok { | ||||
| 				continue | ||||
| 			} | ||||
| 			id, ok := ce.Fun.(*ast.Ident) | ||||
| 			if !ok { | ||||
| 				continue | ||||
| 			} | ||||
| 			typ = id.Name | ||||
| 		} | ||||
| 		if vspec.Type != nil { | ||||
| 			// "X T". We have a type. Remember it.
 | ||||
| 			ident, ok := vspec.Type.(*ast.Ident) | ||||
| 			if !ok { | ||||
| 				continue | ||||
| 			} | ||||
| 			typ = ident.Name | ||||
| 		} | ||||
| 		if typ != f.typeName { | ||||
| 			// This is not the type we're looking for.
 | ||||
| 			continue | ||||
| 		} | ||||
| 		// We now have a list of names (from one line of source code) all being
 | ||||
| 		// declared with the desired type.
 | ||||
| 		// Grab their names and actual values and store them in f.values.
 | ||||
| 		for _, name := range vspec.Names { | ||||
| 			if name.Name == "_" { | ||||
| 				continue | ||||
| 			} | ||||
| 			// This dance lets the type checker find the values for us. It's a
 | ||||
| 			// bit tricky: look up the object declared by the name, find its
 | ||||
| 			// types.Const, and extract its value.
 | ||||
| 			obj, ok := f.pkg.defs[name] | ||||
| 			if !ok { | ||||
| 				log.Fatalf("no value for constant %s", name) | ||||
| 			} | ||||
| 			info := obj.Type().Underlying().(*types.Basic).Info() | ||||
| 			if info&types.IsInteger == 0 { | ||||
| 				log.Fatalf("can't handle non-integer constant type %s", typ) | ||||
| 			} | ||||
| 			value := obj.(*types.Const).Val() // Guaranteed to succeed as this is CONST.
 | ||||
| 			if value.Kind() != constant.Int { | ||||
| 				log.Fatalf("can't happen: constant is not an integer %s", name) | ||||
| 			} | ||||
| 			i64, isInt := constant.Int64Val(value) | ||||
| 			u64, isUint := constant.Uint64Val(value) | ||||
| 			if !isInt && !isUint { | ||||
| 				log.Fatalf("internal error: value of %s is not an integer: %s", name, value.String()) | ||||
| 			} | ||||
| 			if !isInt { | ||||
| 				u64 = uint64(i64) | ||||
| 			} | ||||
| 			v := Value{ | ||||
| 				originalName: name.Name, | ||||
| 				value:        u64, | ||||
| 				signed:       info&types.IsUnsigned == 0, | ||||
| 				str:          value.String(), | ||||
| 			} | ||||
| 			if c := vspec.Comment; f.lineComment && c != nil && len(c.List) == 1 { | ||||
| 				v.name = strings.TrimSpace(c.Text()) | ||||
| 			} else { | ||||
| 				v.name = strings.TrimPrefix(v.originalName, f.trimPrefix) | ||||
| 			} | ||||
| 			f.values = append(f.values, v) | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| // Helpers
 | ||||
| 
 | ||||
| // usize returns the number of bits of the smallest unsigned integer
 | ||||
| // type that will hold n. Used to create the smallest possible slice of
 | ||||
| // integers to use as indexes into the concatenated strings.
 | ||||
| func usize(n int) int { | ||||
| 	switch { | ||||
| 	case n < 1<<8: | ||||
| 		return 8 | ||||
| 	case n < 1<<16: | ||||
| 		return 16 | ||||
| 	default: | ||||
| 		// 2^32 is enough constants for anyone.
 | ||||
| 		return 32 | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // declareIndexAndNameVars declares the index slices and concatenated names
 | ||||
| // strings representing the runs of values.
 | ||||
| func (g *Generator) declareIndexAndNameVars(runs [][]Value, typeName string) { | ||||
| 	var indexes, names []string | ||||
| 	for i, run := range runs { | ||||
| 		index, name := g.createIndexAndNameDecl(run, typeName, fmt.Sprintf("_%d", i)) | ||||
| 		if len(run) != 1 { | ||||
| 			indexes = append(indexes, index) | ||||
| 		} | ||||
| 		names = append(names, name) | ||||
| 	} | ||||
| 	g.Printf("const (\n") | ||||
| 	for _, name := range names { | ||||
| 		g.Printf("\t%s\n", name) | ||||
| 	} | ||||
| 	g.Printf(")\n\n") | ||||
| 
 | ||||
| 	if len(indexes) > 0 { | ||||
| 		g.Printf("var (") | ||||
| 		for _, index := range indexes { | ||||
| 			g.Printf("\t%s\n", index) | ||||
| 		} | ||||
| 		g.Printf(")\n\n") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // declareIndexAndNameVar is the single-run version of declareIndexAndNameVars
 | ||||
| func (g *Generator) declareIndexAndNameVar(run []Value, typeName string) { | ||||
| 	index, name := g.createIndexAndNameDecl(run, typeName, "") | ||||
| 	g.Printf("const %s\n", name) | ||||
| 	g.Printf("var %s\n", index) | ||||
| } | ||||
| 
 | ||||
| // createIndexAndNameDecl returns the pair of declarations for the run. The caller will add "const" and "var".
 | ||||
| func (g *Generator) createIndexAndNameDecl(run []Value, typeName string, suffix string) (string, string) { | ||||
| 	b := new(bytes.Buffer) | ||||
| 	indexes := make([]int, len(run)) | ||||
| 	for i := range run { | ||||
| 		b.WriteString(run[i].name) | ||||
| 		indexes[i] = b.Len() | ||||
| 	} | ||||
| 	nameConst := fmt.Sprintf("_%s_name%s = %q", typeName, suffix, b.String()) | ||||
| 	nameLen := b.Len() | ||||
| 	b.Reset() | ||||
| 	fmt.Fprintf(b, "_%s_index%s = [...]uint%d{0, ", typeName, suffix, usize(nameLen)) | ||||
| 	for i, v := range indexes { | ||||
| 		if i > 0 { | ||||
| 			fmt.Fprintf(b, ", ") | ||||
| 		} | ||||
| 		fmt.Fprintf(b, "%d", v) | ||||
| 	} | ||||
| 	fmt.Fprintf(b, "}") | ||||
| 	return b.String(), nameConst | ||||
| } | ||||
| 
 | ||||
| // declareNameVars declares the concatenated names string representing all the values in the runs.
 | ||||
| func (g *Generator) declareNameVars(runs [][]Value, typeName string, suffix string) { | ||||
| 	g.Printf("const _%s_name%s = \"", typeName, suffix) | ||||
| 	for _, run := range runs { | ||||
| 		for i := range run { | ||||
| 			g.Printf("%s", run[i].name) | ||||
| 		} | ||||
| 	} | ||||
| 	g.Printf("\"\n") | ||||
| } | ||||
| 
 | ||||
| // buildOneRun generates the variables and String method for a single run of contiguous values.
 | ||||
| func (g *Generator) buildOneRun(runs [][]Value, typeName string) { | ||||
| 	values := runs[0] | ||||
| 	g.Printf("\n") | ||||
| 	g.declareIndexAndNameVar(values, typeName) | ||||
| 	// The generated code is simple enough to write as a Printf format.
 | ||||
| 	lessThanZero := "" | ||||
| 	if values[0].signed { | ||||
| 		lessThanZero = "i < 0 || " | ||||
| 	} | ||||
| 	if values[0].value == 0 { // Signed or unsigned, 0 is still 0.
 | ||||
| 		g.Printf(stringOneRun, typeName, usize(len(values)), lessThanZero) | ||||
| 	} else { | ||||
| 		g.Printf(stringOneRunWithOffset, typeName, values[0].String(), usize(len(values)), lessThanZero) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Arguments to format are:
 | ||||
| //
 | ||||
| //	[1]: type name
 | ||||
| //	[2]: size of index element (8 for uint8 etc.)
 | ||||
| //	[3]: less than zero check (for signed types)
 | ||||
| const stringOneRun = `func (i %[1]s) String() string { | ||||
| 	if %[3]si >= %[1]s(len(_%[1]s_index)-1) { | ||||
| 		return "%[1]s(" + strconv.FormatInt(int64(i), 10) + ")" | ||||
| 	} | ||||
| 	return _%[1]s_name[_%[1]s_index[i]:_%[1]s_index[i+1]] | ||||
| } | ||||
| ` | ||||
| 
 | ||||
| // Arguments to format are:
 | ||||
| //	[1]: type name
 | ||||
| //	[2]: lowest defined value for type, as a string
 | ||||
| //	[3]: size of index element (8 for uint8 etc.)
 | ||||
| //	[4]: less than zero check (for signed types)
 | ||||
| /* | ||||
|  */ | ||||
| const stringOneRunWithOffset = `func (i %[1]s) String() string { | ||||
| 	i -= %[2]s | ||||
| 	if %[4]si >= %[1]s(len(_%[1]s_index)-1) { | ||||
| 		return "%[1]s(" + strconv.FormatInt(int64(i + %[2]s), 10) + ")" | ||||
| 	} | ||||
| 	return _%[1]s_name[_%[1]s_index[i] : _%[1]s_index[i+1]] | ||||
| } | ||||
| ` | ||||
| 
 | ||||
| // buildMultipleRuns generates the variables and String method for multiple runs of contiguous values.
 | ||||
| // For this pattern, a single Printf format won't do.
 | ||||
| func (g *Generator) buildMultipleRuns(runs [][]Value, typeName string) { | ||||
| 	g.Printf("\n") | ||||
| 	g.declareIndexAndNameVars(runs, typeName) | ||||
| 	g.Printf("func (i %s) String() string {\n", typeName) | ||||
| 	g.Printf("\tswitch {\n") | ||||
| 	for i, values := range runs { | ||||
| 		if len(values) == 1 { | ||||
| 			g.Printf("\tcase i == %s:\n", &values[0]) | ||||
| 			g.Printf("\t\treturn _%s_name_%d\n", typeName, i) | ||||
| 			continue | ||||
| 		} | ||||
| 		if values[0].value == 0 && !values[0].signed { | ||||
| 			// For an unsigned lower bound of 0, "0 <= i" would be redundant.
 | ||||
| 			g.Printf("\tcase i <= %s:\n", &values[len(values)-1]) | ||||
| 		} else { | ||||
| 			g.Printf("\tcase %s <= i && i <= %s:\n", &values[0], &values[len(values)-1]) | ||||
| 		} | ||||
| 		if values[0].value != 0 { | ||||
| 			g.Printf("\t\ti -= %s\n", &values[0]) | ||||
| 		} | ||||
| 		g.Printf("\t\treturn _%s_name_%d[_%s_index_%d[i]:_%s_index_%d[i+1]]\n", | ||||
| 			typeName, i, typeName, i, typeName, i) | ||||
| 	} | ||||
| 	g.Printf("\tdefault:\n") | ||||
| 	g.Printf("\t\treturn \"%s(\" + strconv.FormatInt(int64(i), 10) + \")\"\n", typeName) | ||||
| 	g.Printf("\t}\n") | ||||
| 	g.Printf("}\n") | ||||
| } | ||||
| 
 | ||||
| // buildMap handles the case where the space is so sparse a map is a reasonable fallback.
 | ||||
| // It's a rare situation but has simple code.
 | ||||
| func (g *Generator) buildMap(runs [][]Value, typeName string) { | ||||
| 	g.Printf("\n") | ||||
| 	g.declareNameVars(runs, typeName, "") | ||||
| 	g.Printf("\nvar _%s_map = map[%s]string{\n", typeName, typeName) | ||||
| 	n := 0 | ||||
| 	for _, values := range runs { | ||||
| 		for _, value := range values { | ||||
| 			g.Printf("\t%s: _%s_name[%d:%d],\n", &value, typeName, n, n+len(value.name)) | ||||
| 			n += len(value.name) | ||||
| 		} | ||||
| 	} | ||||
| 	g.Printf("}\n\n") | ||||
| 	g.Printf(stringMap, typeName) | ||||
| } | ||||
| 
 | ||||
| // Argument to format is the type name.
 | ||||
| const stringMap = `func (i %[1]s) String() string { | ||||
| 	if str, ok := _%[1]s_map[i]; ok { | ||||
| 		return str | ||||
| 	} | ||||
| 	return "%[1]s(" + strconv.FormatInt(int64(i), 10) + ")" | ||||
| } | ||||
| ` | ||||
|  | @ -0,0 +1,177 @@ | |||
| // Copyright 2016 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.
 | ||||
| 
 | ||||
| // Package gcexportdata provides functions for locating, reading, and
 | ||||
| // writing export data files containing type information produced by the
 | ||||
| // gc compiler.  This package supports go1.7 export data format and all
 | ||||
| // later versions.
 | ||||
| //
 | ||||
| // Although it might seem convenient for this package to live alongside
 | ||||
| // go/types in the standard library, this would cause version skew
 | ||||
| // problems for developer tools that use it, since they must be able to
 | ||||
| // consume the outputs of the gc compiler both before and after a Go
 | ||||
| // update such as from Go 1.7 to Go 1.8.  Because this package lives in
 | ||||
| // golang.org/x/tools, sites can update their version of this repo some
 | ||||
| // time before the Go 1.8 release and rebuild and redeploy their
 | ||||
| // developer tools, which will then be able to consume both Go 1.7 and
 | ||||
| // Go 1.8 export data files, so they will work before and after the
 | ||||
| // Go update. (See discussion at https://golang.org/issue/15651.)
 | ||||
| package gcexportdata // import "golang.org/x/tools/go/gcexportdata"
 | ||||
| 
 | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"go/token" | ||||
| 	"go/types" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"os/exec" | ||||
| 
 | ||||
| 	"golang.org/x/tools/go/internal/gcimporter" | ||||
| ) | ||||
| 
 | ||||
| // Find returns the name of an object (.o) or archive (.a) file
 | ||||
| // containing type information for the specified import path,
 | ||||
| // using the go command.
 | ||||
| // If no file was found, an empty filename is returned.
 | ||||
| //
 | ||||
| // A relative srcDir is interpreted relative to the current working directory.
 | ||||
| //
 | ||||
| // Find also returns the package's resolved (canonical) import path,
 | ||||
| // reflecting the effects of srcDir and vendoring on importPath.
 | ||||
| //
 | ||||
| // Deprecated: Use the higher-level API in golang.org/x/tools/go/packages,
 | ||||
| // which is more efficient.
 | ||||
| func Find(importPath, srcDir string) (filename, path string) { | ||||
| 	cmd := exec.Command("go", "list", "-json", "-export", "--", importPath) | ||||
| 	cmd.Dir = srcDir | ||||
| 	out, err := cmd.CombinedOutput() | ||||
| 	if err != nil { | ||||
| 		return "", "" | ||||
| 	} | ||||
| 	var data struct { | ||||
| 		ImportPath string | ||||
| 		Export     string | ||||
| 	} | ||||
| 	json.Unmarshal(out, &data) | ||||
| 	return data.Export, data.ImportPath | ||||
| } | ||||
| 
 | ||||
| // NewReader returns a reader for the export data section of an object
 | ||||
| // (.o) or archive (.a) file read from r.  The new reader may provide
 | ||||
| // additional trailing data beyond the end of the export data.
 | ||||
| func NewReader(r io.Reader) (io.Reader, error) { | ||||
| 	buf := bufio.NewReader(r) | ||||
| 	_, size, err := gcimporter.FindExportData(buf) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	if size >= 0 { | ||||
| 		// We were given an archive and found the __.PKGDEF in it.
 | ||||
| 		// This tells us the size of the export data, and we don't
 | ||||
| 		// need to return the entire file.
 | ||||
| 		return &io.LimitedReader{ | ||||
| 			R: buf, | ||||
| 			N: size, | ||||
| 		}, nil | ||||
| 	} else { | ||||
| 		// We were given an object file. As such, we don't know how large
 | ||||
| 		// the export data is and must return the entire file.
 | ||||
| 		return buf, nil | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Read reads export data from in, decodes it, and returns type
 | ||||
| // information for the package.
 | ||||
| // The package name is specified by path.
 | ||||
| // File position information is added to fset.
 | ||||
| //
 | ||||
| // Read may inspect and add to the imports map to ensure that references
 | ||||
| // within the export data to other packages are consistent.  The caller
 | ||||
| // must ensure that imports[path] does not exist, or exists but is
 | ||||
| // incomplete (see types.Package.Complete), and Read inserts the
 | ||||
| // resulting package into this map entry.
 | ||||
| //
 | ||||
| // On return, the state of the reader is undefined.
 | ||||
| func Read(in io.Reader, fset *token.FileSet, imports map[string]*types.Package, path string) (*types.Package, error) { | ||||
| 	data, err := ioutil.ReadAll(in) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("reading export data for %q: %v", path, err) | ||||
| 	} | ||||
| 
 | ||||
| 	if bytes.HasPrefix(data, []byte("!<arch>")) { | ||||
| 		return nil, fmt.Errorf("can't read export data for %q directly from an archive file (call gcexportdata.NewReader first to extract export data)", path) | ||||
| 	} | ||||
| 
 | ||||
| 	// The App Engine Go runtime v1.6 uses the old export data format.
 | ||||
| 	// TODO(adonovan): delete once v1.7 has been around for a while.
 | ||||
| 	if bytes.HasPrefix(data, []byte("package ")) { | ||||
| 		return gcimporter.ImportData(imports, path, path, bytes.NewReader(data)) | ||||
| 	} | ||||
| 
 | ||||
| 	// The indexed export format starts with an 'i'; the older
 | ||||
| 	// binary export format starts with a 'c', 'd', or 'v'
 | ||||
| 	// (from "version"). Select appropriate importer.
 | ||||
| 	if len(data) > 0 { | ||||
| 		switch data[0] { | ||||
| 		case 'i': | ||||
| 			_, pkg, err := gcimporter.IImportData(fset, imports, data[1:], path) | ||||
| 			return pkg, err | ||||
| 
 | ||||
| 		case 'v', 'c', 'd': | ||||
| 			_, pkg, err := gcimporter.BImportData(fset, imports, data, path) | ||||
| 			return pkg, err | ||||
| 
 | ||||
| 		case 'u': | ||||
| 			_, pkg, err := gcimporter.UImportData(fset, imports, data[1:], path) | ||||
| 			return pkg, err | ||||
| 
 | ||||
| 		default: | ||||
| 			l := len(data) | ||||
| 			if l > 10 { | ||||
| 				l = 10 | ||||
| 			} | ||||
| 			return nil, fmt.Errorf("unexpected export data with prefix %q for path %s", string(data[:l]), path) | ||||
| 		} | ||||
| 	} | ||||
| 	return nil, fmt.Errorf("empty export data for %s", path) | ||||
| } | ||||
| 
 | ||||
| // Write writes encoded type information for the specified package to out.
 | ||||
| // The FileSet provides file position information for named objects.
 | ||||
| func Write(out io.Writer, fset *token.FileSet, pkg *types.Package) error { | ||||
| 	if _, err := io.WriteString(out, "i"); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return gcimporter.IExportData(out, fset, pkg) | ||||
| } | ||||
| 
 | ||||
| // ReadBundle reads an export bundle from in, decodes it, and returns type
 | ||||
| // information for the packages.
 | ||||
| // File position information is added to fset.
 | ||||
| //
 | ||||
| // ReadBundle may inspect and add to the imports map to ensure that references
 | ||||
| // within the export bundle to other packages are consistent.
 | ||||
| //
 | ||||
| // On return, the state of the reader is undefined.
 | ||||
| //
 | ||||
| // Experimental: This API is experimental and may change in the future.
 | ||||
| func ReadBundle(in io.Reader, fset *token.FileSet, imports map[string]*types.Package) ([]*types.Package, error) { | ||||
| 	data, err := ioutil.ReadAll(in) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("reading export bundle: %v", err) | ||||
| 	} | ||||
| 	return gcimporter.IImportBundle(fset, imports, data) | ||||
| } | ||||
| 
 | ||||
| // WriteBundle writes encoded type information for the specified packages to out.
 | ||||
| // The FileSet provides file position information for named objects.
 | ||||
| //
 | ||||
| // Experimental: This API is experimental and may change in the future.
 | ||||
| func WriteBundle(out io.Writer, fset *token.FileSet, pkgs []*types.Package) error { | ||||
| 	return gcimporter.IExportBundle(out, fset, pkgs) | ||||
| } | ||||
|  | @ -0,0 +1,75 @@ | |||
| // Copyright 2016 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.
 | ||||
| 
 | ||||
| package gcexportdata | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"go/token" | ||||
| 	"go/types" | ||||
| 	"os" | ||||
| ) | ||||
| 
 | ||||
| // NewImporter returns a new instance of the types.Importer interface
 | ||||
| // that reads type information from export data files written by gc.
 | ||||
| // The Importer also satisfies types.ImporterFrom.
 | ||||
| //
 | ||||
| // Export data files are located using "go build" workspace conventions
 | ||||
| // and the build.Default context.
 | ||||
| //
 | ||||
| // Use this importer instead of go/importer.For("gc", ...) to avoid the
 | ||||
| // version-skew problems described in the documentation of this package,
 | ||||
| // or to control the FileSet or access the imports map populated during
 | ||||
| // package loading.
 | ||||
| //
 | ||||
| // Deprecated: Use the higher-level API in golang.org/x/tools/go/packages,
 | ||||
| // which is more efficient.
 | ||||
| func NewImporter(fset *token.FileSet, imports map[string]*types.Package) types.ImporterFrom { | ||||
| 	return importer{fset, imports} | ||||
| } | ||||
| 
 | ||||
| type importer struct { | ||||
| 	fset    *token.FileSet | ||||
| 	imports map[string]*types.Package | ||||
| } | ||||
| 
 | ||||
| func (imp importer) Import(importPath string) (*types.Package, error) { | ||||
| 	return imp.ImportFrom(importPath, "", 0) | ||||
| } | ||||
| 
 | ||||
| func (imp importer) ImportFrom(importPath, srcDir string, mode types.ImportMode) (_ *types.Package, err error) { | ||||
| 	filename, path := Find(importPath, srcDir) | ||||
| 	if filename == "" { | ||||
| 		if importPath == "unsafe" { | ||||
| 			// Even for unsafe, call Find first in case
 | ||||
| 			// the package was vendored.
 | ||||
| 			return types.Unsafe, nil | ||||
| 		} | ||||
| 		return nil, fmt.Errorf("can't find import: %s", importPath) | ||||
| 	} | ||||
| 
 | ||||
| 	if pkg, ok := imp.imports[path]; ok && pkg.Complete() { | ||||
| 		return pkg, nil // cache hit
 | ||||
| 	} | ||||
| 
 | ||||
| 	// open file
 | ||||
| 	f, err := os.Open(filename) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer func() { | ||||
| 		f.Close() | ||||
| 		if err != nil { | ||||
| 			// add file name to error
 | ||||
| 			err = fmt.Errorf("reading export data: %s: %v", filename, err) | ||||
| 		} | ||||
| 	}() | ||||
| 
 | ||||
| 	r, err := NewReader(f) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return Read(r, imp.fset, imp.imports, path) | ||||
| } | ||||
|  | @ -0,0 +1,853 @@ | |||
| // Copyright 2016 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.
 | ||||
| 
 | ||||
| // Binary package export.
 | ||||
| // This file was derived from $GOROOT/src/cmd/compile/internal/gc/bexport.go;
 | ||||
| // see that file for specification of the format.
 | ||||
| 
 | ||||
| package gcimporter | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/binary" | ||||
| 	"fmt" | ||||
| 	"go/ast" | ||||
| 	"go/constant" | ||||
| 	"go/token" | ||||
| 	"go/types" | ||||
| 	"math" | ||||
| 	"math/big" | ||||
| 	"sort" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // If debugFormat is set, each integer and string value is preceded by a marker
 | ||||
| // and position information in the encoding. This mechanism permits an importer
 | ||||
| // to recognize immediately when it is out of sync. The importer recognizes this
 | ||||
| // mode automatically (i.e., it can import export data produced with debugging
 | ||||
| // support even if debugFormat is not set at the time of import). This mode will
 | ||||
| // lead to massively larger export data (by a factor of 2 to 3) and should only
 | ||||
| // be enabled during development and debugging.
 | ||||
| //
 | ||||
| // NOTE: This flag is the first flag to enable if importing dies because of
 | ||||
| // (suspected) format errors, and whenever a change is made to the format.
 | ||||
| const debugFormat = false // default: false
 | ||||
| 
 | ||||
| // Current export format version. Increase with each format change.
 | ||||
| //
 | ||||
| // Note: The latest binary (non-indexed) export format is at version 6.
 | ||||
| // This exporter is still at level 4, but it doesn't matter since
 | ||||
| // the binary importer can handle older versions just fine.
 | ||||
| //
 | ||||
| //	6: package height (CL 105038) -- NOT IMPLEMENTED HERE
 | ||||
| //	5: improved position encoding efficiency (issue 20080, CL 41619) -- NOT IMPLEMENTED HERE
 | ||||
| //	4: type name objects support type aliases, uses aliasTag
 | ||||
| //	3: Go1.8 encoding (same as version 2, aliasTag defined but never used)
 | ||||
| //	2: removed unused bool in ODCL export (compiler only)
 | ||||
| //	1: header format change (more regular), export package for _ struct fields
 | ||||
| //	0: Go1.7 encoding
 | ||||
| const exportVersion = 4 | ||||
| 
 | ||||
| // trackAllTypes enables cycle tracking for all types, not just named
 | ||||
| // types. The existing compiler invariants assume that unnamed types
 | ||||
| // that are not completely set up are not used, or else there are spurious
 | ||||
| // errors.
 | ||||
| // If disabled, only named types are tracked, possibly leading to slightly
 | ||||
| // less efficient encoding in rare cases. It also prevents the export of
 | ||||
| // some corner-case type declarations (but those are not handled correctly
 | ||||
| // with with the textual export format either).
 | ||||
| // TODO(gri) enable and remove once issues caused by it are fixed
 | ||||
| const trackAllTypes = false | ||||
| 
 | ||||
| type exporter struct { | ||||
| 	fset *token.FileSet | ||||
| 	out  bytes.Buffer | ||||
| 
 | ||||
| 	// object -> index maps, indexed in order of serialization
 | ||||
| 	strIndex map[string]int | ||||
| 	pkgIndex map[*types.Package]int | ||||
| 	typIndex map[types.Type]int | ||||
| 
 | ||||
| 	// position encoding
 | ||||
| 	posInfoFormat bool | ||||
| 	prevFile      string | ||||
| 	prevLine      int | ||||
| 
 | ||||
| 	// debugging support
 | ||||
| 	written int // bytes written
 | ||||
| 	indent  int // for trace
 | ||||
| } | ||||
| 
 | ||||
| // internalError represents an error generated inside this package.
 | ||||
| type internalError string | ||||
| 
 | ||||
| func (e internalError) Error() string { return "gcimporter: " + string(e) } | ||||
| 
 | ||||
| func internalErrorf(format string, args ...interface{}) error { | ||||
| 	return internalError(fmt.Sprintf(format, args...)) | ||||
| } | ||||
| 
 | ||||
| // BExportData returns binary export data for pkg.
 | ||||
| // If no file set is provided, position info will be missing.
 | ||||
| func BExportData(fset *token.FileSet, pkg *types.Package) (b []byte, err error) { | ||||
| 	if !debug { | ||||
| 		defer func() { | ||||
| 			if e := recover(); e != nil { | ||||
| 				if ierr, ok := e.(internalError); ok { | ||||
| 					err = ierr | ||||
| 					return | ||||
| 				} | ||||
| 				// Not an internal error; panic again.
 | ||||
| 				panic(e) | ||||
| 			} | ||||
| 		}() | ||||
| 	} | ||||
| 
 | ||||
| 	p := exporter{ | ||||
| 		fset:          fset, | ||||
| 		strIndex:      map[string]int{"": 0}, // empty string is mapped to 0
 | ||||
| 		pkgIndex:      make(map[*types.Package]int), | ||||
| 		typIndex:      make(map[types.Type]int), | ||||
| 		posInfoFormat: true, // TODO(gri) might become a flag, eventually
 | ||||
| 	} | ||||
| 
 | ||||
| 	// write version info
 | ||||
| 	// The version string must start with "version %d" where %d is the version
 | ||||
| 	// number. Additional debugging information may follow after a blank; that
 | ||||
| 	// text is ignored by the importer.
 | ||||
| 	p.rawStringln(fmt.Sprintf("version %d", exportVersion)) | ||||
| 	var debug string | ||||
| 	if debugFormat { | ||||
| 		debug = "debug" | ||||
| 	} | ||||
| 	p.rawStringln(debug) // cannot use p.bool since it's affected by debugFormat; also want to see this clearly
 | ||||
| 	p.bool(trackAllTypes) | ||||
| 	p.bool(p.posInfoFormat) | ||||
| 
 | ||||
| 	// --- generic export data ---
 | ||||
| 
 | ||||
| 	// populate type map with predeclared "known" types
 | ||||
| 	for index, typ := range predeclared() { | ||||
| 		p.typIndex[typ] = index | ||||
| 	} | ||||
| 	if len(p.typIndex) != len(predeclared()) { | ||||
| 		return nil, internalError("duplicate entries in type map?") | ||||
| 	} | ||||
| 
 | ||||
| 	// write package data
 | ||||
| 	p.pkg(pkg, true) | ||||
| 	if trace { | ||||
| 		p.tracef("\n") | ||||
| 	} | ||||
| 
 | ||||
| 	// write objects
 | ||||
| 	objcount := 0 | ||||
| 	scope := pkg.Scope() | ||||
| 	for _, name := range scope.Names() { | ||||
| 		if !ast.IsExported(name) { | ||||
| 			continue | ||||
| 		} | ||||
| 		if trace { | ||||
| 			p.tracef("\n") | ||||
| 		} | ||||
| 		p.obj(scope.Lookup(name)) | ||||
| 		objcount++ | ||||
| 	} | ||||
| 
 | ||||
| 	// indicate end of list
 | ||||
| 	if trace { | ||||
| 		p.tracef("\n") | ||||
| 	} | ||||
| 	p.tag(endTag) | ||||
| 
 | ||||
| 	// for self-verification only (redundant)
 | ||||
| 	p.int(objcount) | ||||
| 
 | ||||
| 	if trace { | ||||
| 		p.tracef("\n") | ||||
| 	} | ||||
| 
 | ||||
| 	// --- end of export data ---
 | ||||
| 
 | ||||
| 	return p.out.Bytes(), nil | ||||
| } | ||||
| 
 | ||||
| func (p *exporter) pkg(pkg *types.Package, emptypath bool) { | ||||
| 	if pkg == nil { | ||||
| 		panic(internalError("unexpected nil pkg")) | ||||
| 	} | ||||
| 
 | ||||
| 	// if we saw the package before, write its index (>= 0)
 | ||||
| 	if i, ok := p.pkgIndex[pkg]; ok { | ||||
| 		p.index('P', i) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// otherwise, remember the package, write the package tag (< 0) and package data
 | ||||
| 	if trace { | ||||
| 		p.tracef("P%d = { ", len(p.pkgIndex)) | ||||
| 		defer p.tracef("} ") | ||||
| 	} | ||||
| 	p.pkgIndex[pkg] = len(p.pkgIndex) | ||||
| 
 | ||||
| 	p.tag(packageTag) | ||||
| 	p.string(pkg.Name()) | ||||
| 	if emptypath { | ||||
| 		p.string("") | ||||
| 	} else { | ||||
| 		p.string(pkg.Path()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (p *exporter) obj(obj types.Object) { | ||||
| 	switch obj := obj.(type) { | ||||
| 	case *types.Const: | ||||
| 		p.tag(constTag) | ||||
| 		p.pos(obj) | ||||
| 		p.qualifiedName(obj) | ||||
| 		p.typ(obj.Type()) | ||||
| 		p.value(obj.Val()) | ||||
| 
 | ||||
| 	case *types.TypeName: | ||||
| 		if obj.IsAlias() { | ||||
| 			p.tag(aliasTag) | ||||
| 			p.pos(obj) | ||||
| 			p.qualifiedName(obj) | ||||
| 		} else { | ||||
| 			p.tag(typeTag) | ||||
| 		} | ||||
| 		p.typ(obj.Type()) | ||||
| 
 | ||||
| 	case *types.Var: | ||||
| 		p.tag(varTag) | ||||
| 		p.pos(obj) | ||||
| 		p.qualifiedName(obj) | ||||
| 		p.typ(obj.Type()) | ||||
| 
 | ||||
| 	case *types.Func: | ||||
| 		p.tag(funcTag) | ||||
| 		p.pos(obj) | ||||
| 		p.qualifiedName(obj) | ||||
| 		sig := obj.Type().(*types.Signature) | ||||
| 		p.paramList(sig.Params(), sig.Variadic()) | ||||
| 		p.paramList(sig.Results(), false) | ||||
| 
 | ||||
| 	default: | ||||
| 		panic(internalErrorf("unexpected object %v (%T)", obj, obj)) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (p *exporter) pos(obj types.Object) { | ||||
| 	if !p.posInfoFormat { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	file, line := p.fileLine(obj) | ||||
| 	if file == p.prevFile { | ||||
| 		// common case: write line delta
 | ||||
| 		// delta == 0 means different file or no line change
 | ||||
| 		delta := line - p.prevLine | ||||
| 		p.int(delta) | ||||
| 		if delta == 0 { | ||||
| 			p.int(-1) // -1 means no file change
 | ||||
| 		} | ||||
| 	} else { | ||||
| 		// different file
 | ||||
| 		p.int(0) | ||||
| 		// Encode filename as length of common prefix with previous
 | ||||
| 		// filename, followed by (possibly empty) suffix. Filenames
 | ||||
| 		// frequently share path prefixes, so this can save a lot
 | ||||
| 		// of space and make export data size less dependent on file
 | ||||
| 		// path length. The suffix is unlikely to be empty because
 | ||||
| 		// file names tend to end in ".go".
 | ||||
| 		n := commonPrefixLen(p.prevFile, file) | ||||
| 		p.int(n)           // n >= 0
 | ||||
| 		p.string(file[n:]) // write suffix only
 | ||||
| 		p.prevFile = file | ||||
| 		p.int(line) | ||||
| 	} | ||||
| 	p.prevLine = line | ||||
| } | ||||
| 
 | ||||
| func (p *exporter) fileLine(obj types.Object) (file string, line int) { | ||||
| 	if p.fset != nil { | ||||
| 		pos := p.fset.Position(obj.Pos()) | ||||
| 		file = pos.Filename | ||||
| 		line = pos.Line | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func commonPrefixLen(a, b string) int { | ||||
| 	if len(a) > len(b) { | ||||
| 		a, b = b, a | ||||
| 	} | ||||
| 	// len(a) <= len(b)
 | ||||
| 	i := 0 | ||||
| 	for i < len(a) && a[i] == b[i] { | ||||
| 		i++ | ||||
| 	} | ||||
| 	return i | ||||
| } | ||||
| 
 | ||||
| func (p *exporter) qualifiedName(obj types.Object) { | ||||
| 	p.string(obj.Name()) | ||||
| 	p.pkg(obj.Pkg(), false) | ||||
| } | ||||
| 
 | ||||
| func (p *exporter) typ(t types.Type) { | ||||
| 	if t == nil { | ||||
| 		panic(internalError("nil type")) | ||||
| 	} | ||||
| 
 | ||||
| 	// Possible optimization: Anonymous pointer types *T where
 | ||||
| 	// T is a named type are common. We could canonicalize all
 | ||||
| 	// such types *T to a single type PT = *T. This would lead
 | ||||
| 	// to at most one *T entry in typIndex, and all future *T's
 | ||||
| 	// would be encoded as the respective index directly. Would
 | ||||
| 	// save 1 byte (pointerTag) per *T and reduce the typIndex
 | ||||
| 	// size (at the cost of a canonicalization map). We can do
 | ||||
| 	// this later, without encoding format change.
 | ||||
| 
 | ||||
| 	// if we saw the type before, write its index (>= 0)
 | ||||
| 	if i, ok := p.typIndex[t]; ok { | ||||
| 		p.index('T', i) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// otherwise, remember the type, write the type tag (< 0) and type data
 | ||||
| 	if trackAllTypes { | ||||
| 		if trace { | ||||
| 			p.tracef("T%d = {>\n", len(p.typIndex)) | ||||
| 			defer p.tracef("<\n} ") | ||||
| 		} | ||||
| 		p.typIndex[t] = len(p.typIndex) | ||||
| 	} | ||||
| 
 | ||||
| 	switch t := t.(type) { | ||||
| 	case *types.Named: | ||||
| 		if !trackAllTypes { | ||||
| 			// if we don't track all types, track named types now
 | ||||
| 			p.typIndex[t] = len(p.typIndex) | ||||
| 		} | ||||
| 
 | ||||
| 		p.tag(namedTag) | ||||
| 		p.pos(t.Obj()) | ||||
| 		p.qualifiedName(t.Obj()) | ||||
| 		p.typ(t.Underlying()) | ||||
| 		if !types.IsInterface(t) { | ||||
| 			p.assocMethods(t) | ||||
| 		} | ||||
| 
 | ||||
| 	case *types.Array: | ||||
| 		p.tag(arrayTag) | ||||
| 		p.int64(t.Len()) | ||||
| 		p.typ(t.Elem()) | ||||
| 
 | ||||
| 	case *types.Slice: | ||||
| 		p.tag(sliceTag) | ||||
| 		p.typ(t.Elem()) | ||||
| 
 | ||||
| 	case *dddSlice: | ||||
| 		p.tag(dddTag) | ||||
| 		p.typ(t.elem) | ||||
| 
 | ||||
| 	case *types.Struct: | ||||
| 		p.tag(structTag) | ||||
| 		p.fieldList(t) | ||||
| 
 | ||||
| 	case *types.Pointer: | ||||
| 		p.tag(pointerTag) | ||||
| 		p.typ(t.Elem()) | ||||
| 
 | ||||
| 	case *types.Signature: | ||||
| 		p.tag(signatureTag) | ||||
| 		p.paramList(t.Params(), t.Variadic()) | ||||
| 		p.paramList(t.Results(), false) | ||||
| 
 | ||||
| 	case *types.Interface: | ||||
| 		p.tag(interfaceTag) | ||||
| 		p.iface(t) | ||||
| 
 | ||||
| 	case *types.Map: | ||||
| 		p.tag(mapTag) | ||||
| 		p.typ(t.Key()) | ||||
| 		p.typ(t.Elem()) | ||||
| 
 | ||||
| 	case *types.Chan: | ||||
| 		p.tag(chanTag) | ||||
| 		p.int(int(3 - t.Dir())) // hack
 | ||||
| 		p.typ(t.Elem()) | ||||
| 
 | ||||
| 	default: | ||||
| 		panic(internalErrorf("unexpected type %T: %s", t, t)) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (p *exporter) assocMethods(named *types.Named) { | ||||
| 	// Sort methods (for determinism).
 | ||||
| 	var methods []*types.Func | ||||
| 	for i := 0; i < named.NumMethods(); i++ { | ||||
| 		methods = append(methods, named.Method(i)) | ||||
| 	} | ||||
| 	sort.Sort(methodsByName(methods)) | ||||
| 
 | ||||
| 	p.int(len(methods)) | ||||
| 
 | ||||
| 	if trace && methods != nil { | ||||
| 		p.tracef("associated methods {>\n") | ||||
| 	} | ||||
| 
 | ||||
| 	for i, m := range methods { | ||||
| 		if trace && i > 0 { | ||||
| 			p.tracef("\n") | ||||
| 		} | ||||
| 
 | ||||
| 		p.pos(m) | ||||
| 		name := m.Name() | ||||
| 		p.string(name) | ||||
| 		if !exported(name) { | ||||
| 			p.pkg(m.Pkg(), false) | ||||
| 		} | ||||
| 
 | ||||
| 		sig := m.Type().(*types.Signature) | ||||
| 		p.paramList(types.NewTuple(sig.Recv()), false) | ||||
| 		p.paramList(sig.Params(), sig.Variadic()) | ||||
| 		p.paramList(sig.Results(), false) | ||||
| 		p.int(0) // dummy value for go:nointerface pragma - ignored by importer
 | ||||
| 	} | ||||
| 
 | ||||
| 	if trace && methods != nil { | ||||
| 		p.tracef("<\n} ") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| type methodsByName []*types.Func | ||||
| 
 | ||||
| func (x methodsByName) Len() int           { return len(x) } | ||||
| func (x methodsByName) Swap(i, j int)      { x[i], x[j] = x[j], x[i] } | ||||
| func (x methodsByName) Less(i, j int) bool { return x[i].Name() < x[j].Name() } | ||||
| 
 | ||||
| func (p *exporter) fieldList(t *types.Struct) { | ||||
| 	if trace && t.NumFields() > 0 { | ||||
| 		p.tracef("fields {>\n") | ||||
| 		defer p.tracef("<\n} ") | ||||
| 	} | ||||
| 
 | ||||
| 	p.int(t.NumFields()) | ||||
| 	for i := 0; i < t.NumFields(); i++ { | ||||
| 		if trace && i > 0 { | ||||
| 			p.tracef("\n") | ||||
| 		} | ||||
| 		p.field(t.Field(i)) | ||||
| 		p.string(t.Tag(i)) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (p *exporter) field(f *types.Var) { | ||||
| 	if !f.IsField() { | ||||
| 		panic(internalError("field expected")) | ||||
| 	} | ||||
| 
 | ||||
| 	p.pos(f) | ||||
| 	p.fieldName(f) | ||||
| 	p.typ(f.Type()) | ||||
| } | ||||
| 
 | ||||
| func (p *exporter) iface(t *types.Interface) { | ||||
| 	// TODO(gri): enable importer to load embedded interfaces,
 | ||||
| 	// then emit Embeddeds and ExplicitMethods separately here.
 | ||||
| 	p.int(0) | ||||
| 
 | ||||
| 	n := t.NumMethods() | ||||
| 	if trace && n > 0 { | ||||
| 		p.tracef("methods {>\n") | ||||
| 		defer p.tracef("<\n} ") | ||||
| 	} | ||||
| 	p.int(n) | ||||
| 	for i := 0; i < n; i++ { | ||||
| 		if trace && i > 0 { | ||||
| 			p.tracef("\n") | ||||
| 		} | ||||
| 		p.method(t.Method(i)) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (p *exporter) method(m *types.Func) { | ||||
| 	sig := m.Type().(*types.Signature) | ||||
| 	if sig.Recv() == nil { | ||||
| 		panic(internalError("method expected")) | ||||
| 	} | ||||
| 
 | ||||
| 	p.pos(m) | ||||
| 	p.string(m.Name()) | ||||
| 	if m.Name() != "_" && !ast.IsExported(m.Name()) { | ||||
| 		p.pkg(m.Pkg(), false) | ||||
| 	} | ||||
| 
 | ||||
| 	// interface method; no need to encode receiver.
 | ||||
| 	p.paramList(sig.Params(), sig.Variadic()) | ||||
| 	p.paramList(sig.Results(), false) | ||||
| } | ||||
| 
 | ||||
| func (p *exporter) fieldName(f *types.Var) { | ||||
| 	name := f.Name() | ||||
| 
 | ||||
| 	if f.Anonymous() { | ||||
| 		// anonymous field - we distinguish between 3 cases:
 | ||||
| 		// 1) field name matches base type name and is exported
 | ||||
| 		// 2) field name matches base type name and is not exported
 | ||||
| 		// 3) field name doesn't match base type name (alias name)
 | ||||
| 		bname := basetypeName(f.Type()) | ||||
| 		if name == bname { | ||||
| 			if ast.IsExported(name) { | ||||
| 				name = "" // 1) we don't need to know the field name or package
 | ||||
| 			} else { | ||||
| 				name = "?" // 2) use unexported name "?" to force package export
 | ||||
| 			} | ||||
| 		} else { | ||||
| 			// 3) indicate alias and export name as is
 | ||||
| 			// (this requires an extra "@" but this is a rare case)
 | ||||
| 			p.string("@") | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	p.string(name) | ||||
| 	if name != "" && !ast.IsExported(name) { | ||||
| 		p.pkg(f.Pkg(), false) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func basetypeName(typ types.Type) string { | ||||
| 	switch typ := deref(typ).(type) { | ||||
| 	case *types.Basic: | ||||
| 		return typ.Name() | ||||
| 	case *types.Named: | ||||
| 		return typ.Obj().Name() | ||||
| 	default: | ||||
| 		return "" // unnamed type
 | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (p *exporter) paramList(params *types.Tuple, variadic bool) { | ||||
| 	// use negative length to indicate unnamed parameters
 | ||||
| 	// (look at the first parameter only since either all
 | ||||
| 	// names are present or all are absent)
 | ||||
| 	n := params.Len() | ||||
| 	if n > 0 && params.At(0).Name() == "" { | ||||
| 		n = -n | ||||
| 	} | ||||
| 	p.int(n) | ||||
| 	for i := 0; i < params.Len(); i++ { | ||||
| 		q := params.At(i) | ||||
| 		t := q.Type() | ||||
| 		if variadic && i == params.Len()-1 { | ||||
| 			t = &dddSlice{t.(*types.Slice).Elem()} | ||||
| 		} | ||||
| 		p.typ(t) | ||||
| 		if n > 0 { | ||||
| 			name := q.Name() | ||||
| 			p.string(name) | ||||
| 			if name != "_" { | ||||
| 				p.pkg(q.Pkg(), false) | ||||
| 			} | ||||
| 		} | ||||
| 		p.string("") // no compiler-specific info
 | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (p *exporter) value(x constant.Value) { | ||||
| 	if trace { | ||||
| 		p.tracef("= ") | ||||
| 	} | ||||
| 
 | ||||
| 	switch x.Kind() { | ||||
| 	case constant.Bool: | ||||
| 		tag := falseTag | ||||
| 		if constant.BoolVal(x) { | ||||
| 			tag = trueTag | ||||
| 		} | ||||
| 		p.tag(tag) | ||||
| 
 | ||||
| 	case constant.Int: | ||||
| 		if v, exact := constant.Int64Val(x); exact { | ||||
| 			// common case: x fits into an int64 - use compact encoding
 | ||||
| 			p.tag(int64Tag) | ||||
| 			p.int64(v) | ||||
| 			return | ||||
| 		} | ||||
| 		// uncommon case: large x - use float encoding
 | ||||
| 		// (powers of 2 will be encoded efficiently with exponent)
 | ||||
| 		p.tag(floatTag) | ||||
| 		p.float(constant.ToFloat(x)) | ||||
| 
 | ||||
| 	case constant.Float: | ||||
| 		p.tag(floatTag) | ||||
| 		p.float(x) | ||||
| 
 | ||||
| 	case constant.Complex: | ||||
| 		p.tag(complexTag) | ||||
| 		p.float(constant.Real(x)) | ||||
| 		p.float(constant.Imag(x)) | ||||
| 
 | ||||
| 	case constant.String: | ||||
| 		p.tag(stringTag) | ||||
| 		p.string(constant.StringVal(x)) | ||||
| 
 | ||||
| 	case constant.Unknown: | ||||
| 		// package contains type errors
 | ||||
| 		p.tag(unknownTag) | ||||
| 
 | ||||
| 	default: | ||||
| 		panic(internalErrorf("unexpected value %v (%T)", x, x)) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (p *exporter) float(x constant.Value) { | ||||
| 	if x.Kind() != constant.Float { | ||||
| 		panic(internalErrorf("unexpected constant %v, want float", x)) | ||||
| 	} | ||||
| 	// extract sign (there is no -0)
 | ||||
| 	sign := constant.Sign(x) | ||||
| 	if sign == 0 { | ||||
| 		// x == 0
 | ||||
| 		p.int(0) | ||||
| 		return | ||||
| 	} | ||||
| 	// x != 0
 | ||||
| 
 | ||||
| 	var f big.Float | ||||
| 	if v, exact := constant.Float64Val(x); exact { | ||||
| 		// float64
 | ||||
| 		f.SetFloat64(v) | ||||
| 	} else if num, denom := constant.Num(x), constant.Denom(x); num.Kind() == constant.Int { | ||||
| 		// TODO(gri): add big.Rat accessor to constant.Value.
 | ||||
| 		r := valueToRat(num) | ||||
| 		f.SetRat(r.Quo(r, valueToRat(denom))) | ||||
| 	} else { | ||||
| 		// Value too large to represent as a fraction => inaccessible.
 | ||||
| 		// TODO(gri): add big.Float accessor to constant.Value.
 | ||||
| 		f.SetFloat64(math.MaxFloat64) // FIXME
 | ||||
| 	} | ||||
| 
 | ||||
| 	// extract exponent such that 0.5 <= m < 1.0
 | ||||
| 	var m big.Float | ||||
| 	exp := f.MantExp(&m) | ||||
| 
 | ||||
| 	// extract mantissa as *big.Int
 | ||||
| 	// - set exponent large enough so mant satisfies mant.IsInt()
 | ||||
| 	// - get *big.Int from mant
 | ||||
| 	m.SetMantExp(&m, int(m.MinPrec())) | ||||
| 	mant, acc := m.Int(nil) | ||||
| 	if acc != big.Exact { | ||||
| 		panic(internalError("internal error")) | ||||
| 	} | ||||
| 
 | ||||
| 	p.int(sign) | ||||
| 	p.int(exp) | ||||
| 	p.string(string(mant.Bytes())) | ||||
| } | ||||
| 
 | ||||
| func valueToRat(x constant.Value) *big.Rat { | ||||
| 	// Convert little-endian to big-endian.
 | ||||
| 	// I can't believe this is necessary.
 | ||||
| 	bytes := constant.Bytes(x) | ||||
| 	for i := 0; i < len(bytes)/2; i++ { | ||||
| 		bytes[i], bytes[len(bytes)-1-i] = bytes[len(bytes)-1-i], bytes[i] | ||||
| 	} | ||||
| 	return new(big.Rat).SetInt(new(big.Int).SetBytes(bytes)) | ||||
| } | ||||
| 
 | ||||
| func (p *exporter) bool(b bool) bool { | ||||
| 	if trace { | ||||
| 		p.tracef("[") | ||||
| 		defer p.tracef("= %v] ", b) | ||||
| 	} | ||||
| 
 | ||||
| 	x := 0 | ||||
| 	if b { | ||||
| 		x = 1 | ||||
| 	} | ||||
| 	p.int(x) | ||||
| 	return b | ||||
| } | ||||
| 
 | ||||
| // ----------------------------------------------------------------------------
 | ||||
| // Low-level encoders
 | ||||
| 
 | ||||
| func (p *exporter) index(marker byte, index int) { | ||||
| 	if index < 0 { | ||||
| 		panic(internalError("invalid index < 0")) | ||||
| 	} | ||||
| 	if debugFormat { | ||||
| 		p.marker('t') | ||||
| 	} | ||||
| 	if trace { | ||||
| 		p.tracef("%c%d ", marker, index) | ||||
| 	} | ||||
| 	p.rawInt64(int64(index)) | ||||
| } | ||||
| 
 | ||||
| func (p *exporter) tag(tag int) { | ||||
| 	if tag >= 0 { | ||||
| 		panic(internalError("invalid tag >= 0")) | ||||
| 	} | ||||
| 	if debugFormat { | ||||
| 		p.marker('t') | ||||
| 	} | ||||
| 	if trace { | ||||
| 		p.tracef("%s ", tagString[-tag]) | ||||
| 	} | ||||
| 	p.rawInt64(int64(tag)) | ||||
| } | ||||
| 
 | ||||
| func (p *exporter) int(x int) { | ||||
| 	p.int64(int64(x)) | ||||
| } | ||||
| 
 | ||||
| func (p *exporter) int64(x int64) { | ||||
| 	if debugFormat { | ||||
| 		p.marker('i') | ||||
| 	} | ||||
| 	if trace { | ||||
| 		p.tracef("%d ", x) | ||||
| 	} | ||||
| 	p.rawInt64(x) | ||||
| } | ||||
| 
 | ||||
| func (p *exporter) string(s string) { | ||||
| 	if debugFormat { | ||||
| 		p.marker('s') | ||||
| 	} | ||||
| 	if trace { | ||||
| 		p.tracef("%q ", s) | ||||
| 	} | ||||
| 	// if we saw the string before, write its index (>= 0)
 | ||||
| 	// (the empty string is mapped to 0)
 | ||||
| 	if i, ok := p.strIndex[s]; ok { | ||||
| 		p.rawInt64(int64(i)) | ||||
| 		return | ||||
| 	} | ||||
| 	// otherwise, remember string and write its negative length and bytes
 | ||||
| 	p.strIndex[s] = len(p.strIndex) | ||||
| 	p.rawInt64(-int64(len(s))) | ||||
| 	for i := 0; i < len(s); i++ { | ||||
| 		p.rawByte(s[i]) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // marker emits a marker byte and position information which makes
 | ||||
| // it easy for a reader to detect if it is "out of sync". Used for
 | ||||
| // debugFormat format only.
 | ||||
| func (p *exporter) marker(m byte) { | ||||
| 	p.rawByte(m) | ||||
| 	// Enable this for help tracking down the location
 | ||||
| 	// of an incorrect marker when running in debugFormat.
 | ||||
| 	if false && trace { | ||||
| 		p.tracef("#%d ", p.written) | ||||
| 	} | ||||
| 	p.rawInt64(int64(p.written)) | ||||
| } | ||||
| 
 | ||||
| // rawInt64 should only be used by low-level encoders.
 | ||||
| func (p *exporter) rawInt64(x int64) { | ||||
| 	var tmp [binary.MaxVarintLen64]byte | ||||
| 	n := binary.PutVarint(tmp[:], x) | ||||
| 	for i := 0; i < n; i++ { | ||||
| 		p.rawByte(tmp[i]) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // rawStringln should only be used to emit the initial version string.
 | ||||
| func (p *exporter) rawStringln(s string) { | ||||
| 	for i := 0; i < len(s); i++ { | ||||
| 		p.rawByte(s[i]) | ||||
| 	} | ||||
| 	p.rawByte('\n') | ||||
| } | ||||
| 
 | ||||
| // rawByte is the bottleneck interface to write to p.out.
 | ||||
| // rawByte escapes b as follows (any encoding does that
 | ||||
| // hides '$'):
 | ||||
| //
 | ||||
| //	'$'  => '|' 'S'
 | ||||
| //	'|'  => '|' '|'
 | ||||
| //
 | ||||
| // Necessary so other tools can find the end of the
 | ||||
| // export data by searching for "$$".
 | ||||
| // rawByte should only be used by low-level encoders.
 | ||||
| func (p *exporter) rawByte(b byte) { | ||||
| 	switch b { | ||||
| 	case '$': | ||||
| 		// write '$' as '|' 'S'
 | ||||
| 		b = 'S' | ||||
| 		fallthrough | ||||
| 	case '|': | ||||
| 		// write '|' as '|' '|'
 | ||||
| 		p.out.WriteByte('|') | ||||
| 		p.written++ | ||||
| 	} | ||||
| 	p.out.WriteByte(b) | ||||
| 	p.written++ | ||||
| } | ||||
| 
 | ||||
| // tracef is like fmt.Printf but it rewrites the format string
 | ||||
| // to take care of indentation.
 | ||||
| func (p *exporter) tracef(format string, args ...interface{}) { | ||||
| 	if strings.ContainsAny(format, "<>\n") { | ||||
| 		var buf bytes.Buffer | ||||
| 		for i := 0; i < len(format); i++ { | ||||
| 			// no need to deal with runes
 | ||||
| 			ch := format[i] | ||||
| 			switch ch { | ||||
| 			case '>': | ||||
| 				p.indent++ | ||||
| 				continue | ||||
| 			case '<': | ||||
| 				p.indent-- | ||||
| 				continue | ||||
| 			} | ||||
| 			buf.WriteByte(ch) | ||||
| 			if ch == '\n' { | ||||
| 				for j := p.indent; j > 0; j-- { | ||||
| 					buf.WriteString(".  ") | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		format = buf.String() | ||||
| 	} | ||||
| 	fmt.Printf(format, args...) | ||||
| } | ||||
| 
 | ||||
| // Debugging support.
 | ||||
| // (tagString is only used when tracing is enabled)
 | ||||
| var tagString = [...]string{ | ||||
| 	// Packages
 | ||||
| 	-packageTag: "package", | ||||
| 
 | ||||
| 	// Types
 | ||||
| 	-namedTag:     "named type", | ||||
| 	-arrayTag:     "array", | ||||
| 	-sliceTag:     "slice", | ||||
| 	-dddTag:       "ddd", | ||||
| 	-structTag:    "struct", | ||||
| 	-pointerTag:   "pointer", | ||||
| 	-signatureTag: "signature", | ||||
| 	-interfaceTag: "interface", | ||||
| 	-mapTag:       "map", | ||||
| 	-chanTag:      "chan", | ||||
| 
 | ||||
| 	// Values
 | ||||
| 	-falseTag:    "false", | ||||
| 	-trueTag:     "true", | ||||
| 	-int64Tag:    "int64", | ||||
| 	-floatTag:    "float", | ||||
| 	-fractionTag: "fraction", | ||||
| 	-complexTag:  "complex", | ||||
| 	-stringTag:   "string", | ||||
| 	-unknownTag:  "unknown", | ||||
| 
 | ||||
| 	// Type aliases
 | ||||
| 	-aliasTag: "alias", | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,99 @@ | |||
| // Copyright 2011 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.
 | ||||
| 
 | ||||
| // This file is a copy of $GOROOT/src/go/internal/gcimporter/exportdata.go.
 | ||||
| 
 | ||||
| // This file implements FindExportData.
 | ||||
| 
 | ||||
| package gcimporter | ||||
| 
 | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| func readGopackHeader(r *bufio.Reader) (name string, size int64, err error) { | ||||
| 	// See $GOROOT/include/ar.h.
 | ||||
| 	hdr := make([]byte, 16+12+6+6+8+10+2) | ||||
| 	_, err = io.ReadFull(r, hdr) | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 	// leave for debugging
 | ||||
| 	if false { | ||||
| 		fmt.Printf("header: %s", hdr) | ||||
| 	} | ||||
| 	s := strings.TrimSpace(string(hdr[16+12+6+6+8:][:10])) | ||||
| 	length, err := strconv.Atoi(s) | ||||
| 	size = int64(length) | ||||
| 	if err != nil || hdr[len(hdr)-2] != '`' || hdr[len(hdr)-1] != '\n' { | ||||
| 		err = fmt.Errorf("invalid archive header") | ||||
| 		return | ||||
| 	} | ||||
| 	name = strings.TrimSpace(string(hdr[:16])) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // FindExportData positions the reader r at the beginning of the
 | ||||
| // export data section of an underlying GC-created object/archive
 | ||||
| // file by reading from it. The reader must be positioned at the
 | ||||
| // start of the file before calling this function. The hdr result
 | ||||
| // is the string before the export data, either "$$" or "$$B".
 | ||||
| // The size result is the length of the export data in bytes, or -1 if not known.
 | ||||
| func FindExportData(r *bufio.Reader) (hdr string, size int64, err error) { | ||||
| 	// Read first line to make sure this is an object file.
 | ||||
| 	line, err := r.ReadSlice('\n') | ||||
| 	if err != nil { | ||||
| 		err = fmt.Errorf("can't find export data (%v)", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if string(line) == "!<arch>\n" { | ||||
| 		// Archive file. Scan to __.PKGDEF.
 | ||||
| 		var name string | ||||
| 		if name, size, err = readGopackHeader(r); err != nil { | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		// First entry should be __.PKGDEF.
 | ||||
| 		if name != "__.PKGDEF" { | ||||
| 			err = fmt.Errorf("go archive is missing __.PKGDEF") | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		// Read first line of __.PKGDEF data, so that line
 | ||||
| 		// is once again the first line of the input.
 | ||||
| 		if line, err = r.ReadSlice('\n'); err != nil { | ||||
| 			err = fmt.Errorf("can't find export data (%v)", err) | ||||
| 			return | ||||
| 		} | ||||
| 		size -= int64(len(line)) | ||||
| 	} | ||||
| 
 | ||||
| 	// Now at __.PKGDEF in archive or still at beginning of file.
 | ||||
| 	// Either way, line should begin with "go object ".
 | ||||
| 	if !strings.HasPrefix(string(line), "go object ") { | ||||
| 		err = fmt.Errorf("not a Go object file") | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// Skip over object header to export data.
 | ||||
| 	// Begins after first line starting with $$.
 | ||||
| 	for line[0] != '$' { | ||||
| 		if line, err = r.ReadSlice('\n'); err != nil { | ||||
| 			err = fmt.Errorf("can't find export data (%v)", err) | ||||
| 			return | ||||
| 		} | ||||
| 		size -= int64(len(line)) | ||||
| 	} | ||||
| 	hdr = string(line) | ||||
| 	if size < 0 { | ||||
| 		size = -1 | ||||
| 	} | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,878 @@ | |||
| // Copyright 2018 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.
 | ||||
| 
 | ||||
| // Indexed package import.
 | ||||
| // See cmd/compile/internal/gc/iexport.go for the export data format.
 | ||||
| 
 | ||||
| // This file is a copy of $GOROOT/src/go/internal/gcimporter/iimport.go.
 | ||||
| 
 | ||||
| package gcimporter | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/binary" | ||||
| 	"fmt" | ||||
| 	"go/constant" | ||||
| 	"go/token" | ||||
| 	"go/types" | ||||
| 	"io" | ||||
| 	"math/big" | ||||
| 	"sort" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"golang.org/x/tools/internal/typeparams" | ||||
| ) | ||||
| 
 | ||||
| type intReader struct { | ||||
| 	*bytes.Reader | ||||
| 	path string | ||||
| } | ||||
| 
 | ||||
| func (r *intReader) int64() int64 { | ||||
| 	i, err := binary.ReadVarint(r.Reader) | ||||
| 	if err != nil { | ||||
| 		errorf("import %q: read varint error: %v", r.path, err) | ||||
| 	} | ||||
| 	return i | ||||
| } | ||||
| 
 | ||||
| func (r *intReader) uint64() uint64 { | ||||
| 	i, err := binary.ReadUvarint(r.Reader) | ||||
| 	if err != nil { | ||||
| 		errorf("import %q: read varint error: %v", r.path, err) | ||||
| 	} | ||||
| 	return i | ||||
| } | ||||
| 
 | ||||
| // Keep this in sync with constants in iexport.go.
 | ||||
| const ( | ||||
| 	iexportVersionGo1_11   = 0 | ||||
| 	iexportVersionPosCol   = 1 | ||||
| 	iexportVersionGo1_18   = 2 | ||||
| 	iexportVersionGenerics = 2 | ||||
| ) | ||||
| 
 | ||||
| type ident struct { | ||||
| 	pkg  *types.Package | ||||
| 	name string | ||||
| } | ||||
| 
 | ||||
| const predeclReserved = 32 | ||||
| 
 | ||||
| type itag uint64 | ||||
| 
 | ||||
| const ( | ||||
| 	// Types
 | ||||
| 	definedType itag = iota | ||||
| 	pointerType | ||||
| 	sliceType | ||||
| 	arrayType | ||||
| 	chanType | ||||
| 	mapType | ||||
| 	signatureType | ||||
| 	structType | ||||
| 	interfaceType | ||||
| 	typeParamType | ||||
| 	instanceType | ||||
| 	unionType | ||||
| ) | ||||
| 
 | ||||
| // IImportData imports a package from the serialized package data
 | ||||
| // and returns 0 and a reference to the package.
 | ||||
| // If the export data version is not recognized or the format is otherwise
 | ||||
| // compromised, an error is returned.
 | ||||
| func IImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (int, *types.Package, error) { | ||||
| 	pkgs, err := iimportCommon(fset, imports, data, false, path) | ||||
| 	if err != nil { | ||||
| 		return 0, nil, err | ||||
| 	} | ||||
| 	return 0, pkgs[0], nil | ||||
| } | ||||
| 
 | ||||
| // IImportBundle imports a set of packages from the serialized package bundle.
 | ||||
| func IImportBundle(fset *token.FileSet, imports map[string]*types.Package, data []byte) ([]*types.Package, error) { | ||||
| 	return iimportCommon(fset, imports, data, true, "") | ||||
| } | ||||
| 
 | ||||
| func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data []byte, bundle bool, path string) (pkgs []*types.Package, err error) { | ||||
| 	const currentVersion = 1 | ||||
| 	version := int64(-1) | ||||
| 	if !debug { | ||||
| 		defer func() { | ||||
| 			if e := recover(); e != nil { | ||||
| 				if bundle { | ||||
| 					err = fmt.Errorf("%v", e) | ||||
| 				} else if version > currentVersion { | ||||
| 					err = fmt.Errorf("cannot import %q (%v), export data is newer version - update tool", path, e) | ||||
| 				} else { | ||||
| 					err = fmt.Errorf("cannot import %q (%v), possibly version skew - reinstall package", path, e) | ||||
| 				} | ||||
| 			} | ||||
| 		}() | ||||
| 	} | ||||
| 
 | ||||
| 	r := &intReader{bytes.NewReader(data), path} | ||||
| 
 | ||||
| 	if bundle { | ||||
| 		bundleVersion := r.uint64() | ||||
| 		switch bundleVersion { | ||||
| 		case bundleVersion: | ||||
| 		default: | ||||
| 			errorf("unknown bundle format version %d", bundleVersion) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	version = int64(r.uint64()) | ||||
| 	switch version { | ||||
| 	case iexportVersionGo1_18, iexportVersionPosCol, iexportVersionGo1_11: | ||||
| 	default: | ||||
| 		if version > iexportVersionGo1_18 { | ||||
| 			errorf("unstable iexport format version %d, just rebuild compiler and std library", version) | ||||
| 		} else { | ||||
| 			errorf("unknown iexport format version %d", version) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	sLen := int64(r.uint64()) | ||||
| 	dLen := int64(r.uint64()) | ||||
| 
 | ||||
| 	whence, _ := r.Seek(0, io.SeekCurrent) | ||||
| 	stringData := data[whence : whence+sLen] | ||||
| 	declData := data[whence+sLen : whence+sLen+dLen] | ||||
| 	r.Seek(sLen+dLen, io.SeekCurrent) | ||||
| 
 | ||||
| 	p := iimporter{ | ||||
| 		version: int(version), | ||||
| 		ipath:   path, | ||||
| 
 | ||||
| 		stringData:  stringData, | ||||
| 		stringCache: make(map[uint64]string), | ||||
| 		pkgCache:    make(map[uint64]*types.Package), | ||||
| 
 | ||||
| 		declData: declData, | ||||
| 		pkgIndex: make(map[*types.Package]map[string]uint64), | ||||
| 		typCache: make(map[uint64]types.Type), | ||||
| 		// Separate map for typeparams, keyed by their package and unique
 | ||||
| 		// name.
 | ||||
| 		tparamIndex: make(map[ident]types.Type), | ||||
| 
 | ||||
| 		fake: fakeFileSet{ | ||||
| 			fset:  fset, | ||||
| 			files: make(map[string]*fileInfo), | ||||
| 		}, | ||||
| 	} | ||||
| 	defer p.fake.setLines() // set lines for files in fset
 | ||||
| 
 | ||||
| 	for i, pt := range predeclared() { | ||||
| 		p.typCache[uint64(i)] = pt | ||||
| 	} | ||||
| 
 | ||||
| 	pkgList := make([]*types.Package, r.uint64()) | ||||
| 	for i := range pkgList { | ||||
| 		pkgPathOff := r.uint64() | ||||
| 		pkgPath := p.stringAt(pkgPathOff) | ||||
| 		pkgName := p.stringAt(r.uint64()) | ||||
| 		_ = r.uint64() // package height; unused by go/types
 | ||||
| 
 | ||||
| 		if pkgPath == "" { | ||||
| 			pkgPath = path | ||||
| 		} | ||||
| 		pkg := imports[pkgPath] | ||||
| 		if pkg == nil { | ||||
| 			pkg = types.NewPackage(pkgPath, pkgName) | ||||
| 			imports[pkgPath] = pkg | ||||
| 		} else if pkg.Name() != pkgName { | ||||
| 			errorf("conflicting names %s and %s for package %q", pkg.Name(), pkgName, path) | ||||
| 		} | ||||
| 
 | ||||
| 		p.pkgCache[pkgPathOff] = pkg | ||||
| 
 | ||||
| 		nameIndex := make(map[string]uint64) | ||||
| 		for nSyms := r.uint64(); nSyms > 0; nSyms-- { | ||||
| 			name := p.stringAt(r.uint64()) | ||||
| 			nameIndex[name] = r.uint64() | ||||
| 		} | ||||
| 
 | ||||
| 		p.pkgIndex[pkg] = nameIndex | ||||
| 		pkgList[i] = pkg | ||||
| 	} | ||||
| 
 | ||||
| 	if bundle { | ||||
| 		pkgs = make([]*types.Package, r.uint64()) | ||||
| 		for i := range pkgs { | ||||
| 			pkg := p.pkgAt(r.uint64()) | ||||
| 			imps := make([]*types.Package, r.uint64()) | ||||
| 			for j := range imps { | ||||
| 				imps[j] = p.pkgAt(r.uint64()) | ||||
| 			} | ||||
| 			pkg.SetImports(imps) | ||||
| 			pkgs[i] = pkg | ||||
| 		} | ||||
| 	} else { | ||||
| 		if len(pkgList) == 0 { | ||||
| 			errorf("no packages found for %s", path) | ||||
| 			panic("unreachable") | ||||
| 		} | ||||
| 		pkgs = pkgList[:1] | ||||
| 
 | ||||
| 		// record all referenced packages as imports
 | ||||
| 		list := append(([]*types.Package)(nil), pkgList[1:]...) | ||||
| 		sort.Sort(byPath(list)) | ||||
| 		pkgs[0].SetImports(list) | ||||
| 	} | ||||
| 
 | ||||
| 	for _, pkg := range pkgs { | ||||
| 		if pkg.Complete() { | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		names := make([]string, 0, len(p.pkgIndex[pkg])) | ||||
| 		for name := range p.pkgIndex[pkg] { | ||||
| 			names = append(names, name) | ||||
| 		} | ||||
| 		sort.Strings(names) | ||||
| 		for _, name := range names { | ||||
| 			p.doDecl(pkg, name) | ||||
| 		} | ||||
| 
 | ||||
| 		// package was imported completely and without errors
 | ||||
| 		pkg.MarkComplete() | ||||
| 	} | ||||
| 
 | ||||
| 	// SetConstraint can't be called if the constraint type is not yet complete.
 | ||||
| 	// When type params are created in the 'P' case of (*importReader).obj(),
 | ||||
| 	// the associated constraint type may not be complete due to recursion.
 | ||||
| 	// Therefore, we defer calling SetConstraint there, and call it here instead
 | ||||
| 	// after all types are complete.
 | ||||
| 	for _, d := range p.later { | ||||
| 		typeparams.SetTypeParamConstraint(d.t, d.constraint) | ||||
| 	} | ||||
| 
 | ||||
| 	for _, typ := range p.interfaceList { | ||||
| 		typ.Complete() | ||||
| 	} | ||||
| 
 | ||||
| 	return pkgs, nil | ||||
| } | ||||
| 
 | ||||
| type setConstraintArgs struct { | ||||
| 	t          *typeparams.TypeParam | ||||
| 	constraint types.Type | ||||
| } | ||||
| 
 | ||||
| type iimporter struct { | ||||
| 	version int | ||||
| 	ipath   string | ||||
| 
 | ||||
| 	stringData  []byte | ||||
| 	stringCache map[uint64]string | ||||
| 	pkgCache    map[uint64]*types.Package | ||||
| 
 | ||||
| 	declData    []byte | ||||
| 	pkgIndex    map[*types.Package]map[string]uint64 | ||||
| 	typCache    map[uint64]types.Type | ||||
| 	tparamIndex map[ident]types.Type | ||||
| 
 | ||||
| 	fake          fakeFileSet | ||||
| 	interfaceList []*types.Interface | ||||
| 
 | ||||
| 	// Arguments for calls to SetConstraint that are deferred due to recursive types
 | ||||
| 	later []setConstraintArgs | ||||
| 
 | ||||
| 	indent int // for tracing support
 | ||||
| } | ||||
| 
 | ||||
| func (p *iimporter) trace(format string, args ...interface{}) { | ||||
| 	if !trace { | ||||
| 		// Call sites should also be guarded, but having this check here allows
 | ||||
| 		// easily enabling/disabling debug trace statements.
 | ||||
| 		return | ||||
| 	} | ||||
| 	fmt.Printf(strings.Repeat("..", p.indent)+format+"\n", args...) | ||||
| } | ||||
| 
 | ||||
| func (p *iimporter) doDecl(pkg *types.Package, name string) { | ||||
| 	if debug { | ||||
| 		p.trace("import decl %s", name) | ||||
| 		p.indent++ | ||||
| 		defer func() { | ||||
| 			p.indent-- | ||||
| 			p.trace("=> %s", name) | ||||
| 		}() | ||||
| 	} | ||||
| 	// See if we've already imported this declaration.
 | ||||
| 	if obj := pkg.Scope().Lookup(name); obj != nil { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	off, ok := p.pkgIndex[pkg][name] | ||||
| 	if !ok { | ||||
| 		errorf("%v.%v not in index", pkg, name) | ||||
| 	} | ||||
| 
 | ||||
| 	r := &importReader{p: p, currPkg: pkg} | ||||
| 	r.declReader.Reset(p.declData[off:]) | ||||
| 
 | ||||
| 	r.obj(name) | ||||
| } | ||||
| 
 | ||||
| func (p *iimporter) stringAt(off uint64) string { | ||||
| 	if s, ok := p.stringCache[off]; ok { | ||||
| 		return s | ||||
| 	} | ||||
| 
 | ||||
| 	slen, n := binary.Uvarint(p.stringData[off:]) | ||||
| 	if n <= 0 { | ||||
| 		errorf("varint failed") | ||||
| 	} | ||||
| 	spos := off + uint64(n) | ||||
| 	s := string(p.stringData[spos : spos+slen]) | ||||
| 	p.stringCache[off] = s | ||||
| 	return s | ||||
| } | ||||
| 
 | ||||
| func (p *iimporter) pkgAt(off uint64) *types.Package { | ||||
| 	if pkg, ok := p.pkgCache[off]; ok { | ||||
| 		return pkg | ||||
| 	} | ||||
| 	path := p.stringAt(off) | ||||
| 	errorf("missing package %q in %q", path, p.ipath) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (p *iimporter) typAt(off uint64, base *types.Named) types.Type { | ||||
| 	if t, ok := p.typCache[off]; ok && canReuse(base, t) { | ||||
| 		return t | ||||
| 	} | ||||
| 
 | ||||
| 	if off < predeclReserved { | ||||
| 		errorf("predeclared type missing from cache: %v", off) | ||||
| 	} | ||||
| 
 | ||||
| 	r := &importReader{p: p} | ||||
| 	r.declReader.Reset(p.declData[off-predeclReserved:]) | ||||
| 	t := r.doType(base) | ||||
| 
 | ||||
| 	if canReuse(base, t) { | ||||
| 		p.typCache[off] = t | ||||
| 	} | ||||
| 	return t | ||||
| } | ||||
| 
 | ||||
| // canReuse reports whether the type rhs on the RHS of the declaration for def
 | ||||
| // may be re-used.
 | ||||
| //
 | ||||
| // Specifically, if def is non-nil and rhs is an interface type with methods, it
 | ||||
| // may not be re-used because we have a convention of setting the receiver type
 | ||||
| // for interface methods to def.
 | ||||
| func canReuse(def *types.Named, rhs types.Type) bool { | ||||
| 	if def == nil { | ||||
| 		return true | ||||
| 	} | ||||
| 	iface, _ := rhs.(*types.Interface) | ||||
| 	if iface == nil { | ||||
| 		return true | ||||
| 	} | ||||
| 	// Don't use iface.Empty() here as iface may not be complete.
 | ||||
| 	return iface.NumEmbeddeds() == 0 && iface.NumExplicitMethods() == 0 | ||||
| } | ||||
| 
 | ||||
| type importReader struct { | ||||
| 	p          *iimporter | ||||
| 	declReader bytes.Reader | ||||
| 	currPkg    *types.Package | ||||
| 	prevFile   string | ||||
| 	prevLine   int64 | ||||
| 	prevColumn int64 | ||||
| } | ||||
| 
 | ||||
| func (r *importReader) obj(name string) { | ||||
| 	tag := r.byte() | ||||
| 	pos := r.pos() | ||||
| 
 | ||||
| 	switch tag { | ||||
| 	case 'A': | ||||
| 		typ := r.typ() | ||||
| 
 | ||||
| 		r.declare(types.NewTypeName(pos, r.currPkg, name, typ)) | ||||
| 
 | ||||
| 	case 'C': | ||||
| 		typ, val := r.value() | ||||
| 
 | ||||
| 		r.declare(types.NewConst(pos, r.currPkg, name, typ, val)) | ||||
| 
 | ||||
| 	case 'F', 'G': | ||||
| 		var tparams []*typeparams.TypeParam | ||||
| 		if tag == 'G' { | ||||
| 			tparams = r.tparamList() | ||||
| 		} | ||||
| 		sig := r.signature(nil, nil, tparams) | ||||
| 		r.declare(types.NewFunc(pos, r.currPkg, name, sig)) | ||||
| 
 | ||||
| 	case 'T', 'U': | ||||
| 		// Types can be recursive. We need to setup a stub
 | ||||
| 		// declaration before recursing.
 | ||||
| 		obj := types.NewTypeName(pos, r.currPkg, name, nil) | ||||
| 		named := types.NewNamed(obj, nil, nil) | ||||
| 		// Declare obj before calling r.tparamList, so the new type name is recognized
 | ||||
| 		// if used in the constraint of one of its own typeparams (see #48280).
 | ||||
| 		r.declare(obj) | ||||
| 		if tag == 'U' { | ||||
| 			tparams := r.tparamList() | ||||
| 			typeparams.SetForNamed(named, tparams) | ||||
| 		} | ||||
| 
 | ||||
| 		underlying := r.p.typAt(r.uint64(), named).Underlying() | ||||
| 		named.SetUnderlying(underlying) | ||||
| 
 | ||||
| 		if !isInterface(underlying) { | ||||
| 			for n := r.uint64(); n > 0; n-- { | ||||
| 				mpos := r.pos() | ||||
| 				mname := r.ident() | ||||
| 				recv := r.param() | ||||
| 
 | ||||
| 				// If the receiver has any targs, set those as the
 | ||||
| 				// rparams of the method (since those are the
 | ||||
| 				// typeparams being used in the method sig/body).
 | ||||
| 				base := baseType(recv.Type()) | ||||
| 				assert(base != nil) | ||||
| 				targs := typeparams.NamedTypeArgs(base) | ||||
| 				var rparams []*typeparams.TypeParam | ||||
| 				if targs.Len() > 0 { | ||||
| 					rparams = make([]*typeparams.TypeParam, targs.Len()) | ||||
| 					for i := range rparams { | ||||
| 						rparams[i] = targs.At(i).(*typeparams.TypeParam) | ||||
| 					} | ||||
| 				} | ||||
| 				msig := r.signature(recv, rparams, nil) | ||||
| 
 | ||||
| 				named.AddMethod(types.NewFunc(mpos, r.currPkg, mname, msig)) | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 	case 'P': | ||||
| 		// We need to "declare" a typeparam in order to have a name that
 | ||||
| 		// can be referenced recursively (if needed) in the type param's
 | ||||
| 		// bound.
 | ||||
| 		if r.p.version < iexportVersionGenerics { | ||||
| 			errorf("unexpected type param type") | ||||
| 		} | ||||
| 		name0 := tparamName(name) | ||||
| 		tn := types.NewTypeName(pos, r.currPkg, name0, nil) | ||||
| 		t := typeparams.NewTypeParam(tn, nil) | ||||
| 
 | ||||
| 		// To handle recursive references to the typeparam within its
 | ||||
| 		// bound, save the partial type in tparamIndex before reading the bounds.
 | ||||
| 		id := ident{r.currPkg, name} | ||||
| 		r.p.tparamIndex[id] = t | ||||
| 		var implicit bool | ||||
| 		if r.p.version >= iexportVersionGo1_18 { | ||||
| 			implicit = r.bool() | ||||
| 		} | ||||
| 		constraint := r.typ() | ||||
| 		if implicit { | ||||
| 			iface, _ := constraint.(*types.Interface) | ||||
| 			if iface == nil { | ||||
| 				errorf("non-interface constraint marked implicit") | ||||
| 			} | ||||
| 			typeparams.MarkImplicit(iface) | ||||
| 		} | ||||
| 		// The constraint type may not be complete, if we
 | ||||
| 		// are in the middle of a type recursion involving type
 | ||||
| 		// constraints. So, we defer SetConstraint until we have
 | ||||
| 		// completely set up all types in ImportData.
 | ||||
| 		r.p.later = append(r.p.later, setConstraintArgs{t: t, constraint: constraint}) | ||||
| 
 | ||||
| 	case 'V': | ||||
| 		typ := r.typ() | ||||
| 
 | ||||
| 		r.declare(types.NewVar(pos, r.currPkg, name, typ)) | ||||
| 
 | ||||
| 	default: | ||||
| 		errorf("unexpected tag: %v", tag) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (r *importReader) declare(obj types.Object) { | ||||
| 	obj.Pkg().Scope().Insert(obj) | ||||
| } | ||||
| 
 | ||||
| func (r *importReader) value() (typ types.Type, val constant.Value) { | ||||
| 	typ = r.typ() | ||||
| 	if r.p.version >= iexportVersionGo1_18 { | ||||
| 		// TODO: add support for using the kind.
 | ||||
| 		_ = constant.Kind(r.int64()) | ||||
| 	} | ||||
| 
 | ||||
| 	switch b := typ.Underlying().(*types.Basic); b.Info() & types.IsConstType { | ||||
| 	case types.IsBoolean: | ||||
| 		val = constant.MakeBool(r.bool()) | ||||
| 
 | ||||
| 	case types.IsString: | ||||
| 		val = constant.MakeString(r.string()) | ||||
| 
 | ||||
| 	case types.IsInteger: | ||||
| 		var x big.Int | ||||
| 		r.mpint(&x, b) | ||||
| 		val = constant.Make(&x) | ||||
| 
 | ||||
| 	case types.IsFloat: | ||||
| 		val = r.mpfloat(b) | ||||
| 
 | ||||
| 	case types.IsComplex: | ||||
| 		re := r.mpfloat(b) | ||||
| 		im := r.mpfloat(b) | ||||
| 		val = constant.BinaryOp(re, token.ADD, constant.MakeImag(im)) | ||||
| 
 | ||||
| 	default: | ||||
| 		if b.Kind() == types.Invalid { | ||||
| 			val = constant.MakeUnknown() | ||||
| 			return | ||||
| 		} | ||||
| 		errorf("unexpected type %v", typ) // panics
 | ||||
| 		panic("unreachable") | ||||
| 	} | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func intSize(b *types.Basic) (signed bool, maxBytes uint) { | ||||
| 	if (b.Info() & types.IsUntyped) != 0 { | ||||
| 		return true, 64 | ||||
| 	} | ||||
| 
 | ||||
| 	switch b.Kind() { | ||||
| 	case types.Float32, types.Complex64: | ||||
| 		return true, 3 | ||||
| 	case types.Float64, types.Complex128: | ||||
| 		return true, 7 | ||||
| 	} | ||||
| 
 | ||||
| 	signed = (b.Info() & types.IsUnsigned) == 0 | ||||
| 	switch b.Kind() { | ||||
| 	case types.Int8, types.Uint8: | ||||
| 		maxBytes = 1 | ||||
| 	case types.Int16, types.Uint16: | ||||
| 		maxBytes = 2 | ||||
| 	case types.Int32, types.Uint32: | ||||
| 		maxBytes = 4 | ||||
| 	default: | ||||
| 		maxBytes = 8 | ||||
| 	} | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (r *importReader) mpint(x *big.Int, typ *types.Basic) { | ||||
| 	signed, maxBytes := intSize(typ) | ||||
| 
 | ||||
| 	maxSmall := 256 - maxBytes | ||||
| 	if signed { | ||||
| 		maxSmall = 256 - 2*maxBytes | ||||
| 	} | ||||
| 	if maxBytes == 1 { | ||||
| 		maxSmall = 256 | ||||
| 	} | ||||
| 
 | ||||
| 	n, _ := r.declReader.ReadByte() | ||||
| 	if uint(n) < maxSmall { | ||||
| 		v := int64(n) | ||||
| 		if signed { | ||||
| 			v >>= 1 | ||||
| 			if n&1 != 0 { | ||||
| 				v = ^v | ||||
| 			} | ||||
| 		} | ||||
| 		x.SetInt64(v) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	v := -n | ||||
| 	if signed { | ||||
| 		v = -(n &^ 1) >> 1 | ||||
| 	} | ||||
| 	if v < 1 || uint(v) > maxBytes { | ||||
| 		errorf("weird decoding: %v, %v => %v", n, signed, v) | ||||
| 	} | ||||
| 	b := make([]byte, v) | ||||
| 	io.ReadFull(&r.declReader, b) | ||||
| 	x.SetBytes(b) | ||||
| 	if signed && n&1 != 0 { | ||||
| 		x.Neg(x) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (r *importReader) mpfloat(typ *types.Basic) constant.Value { | ||||
| 	var mant big.Int | ||||
| 	r.mpint(&mant, typ) | ||||
| 	var f big.Float | ||||
| 	f.SetInt(&mant) | ||||
| 	if f.Sign() != 0 { | ||||
| 		f.SetMantExp(&f, int(r.int64())) | ||||
| 	} | ||||
| 	return constant.Make(&f) | ||||
| } | ||||
| 
 | ||||
| func (r *importReader) ident() string { | ||||
| 	return r.string() | ||||
| } | ||||
| 
 | ||||
| func (r *importReader) qualifiedIdent() (*types.Package, string) { | ||||
| 	name := r.string() | ||||
| 	pkg := r.pkg() | ||||
| 	return pkg, name | ||||
| } | ||||
| 
 | ||||
| func (r *importReader) pos() token.Pos { | ||||
| 	if r.p.version >= iexportVersionPosCol { | ||||
| 		r.posv1() | ||||
| 	} else { | ||||
| 		r.posv0() | ||||
| 	} | ||||
| 
 | ||||
| 	if r.prevFile == "" && r.prevLine == 0 && r.prevColumn == 0 { | ||||
| 		return token.NoPos | ||||
| 	} | ||||
| 	return r.p.fake.pos(r.prevFile, int(r.prevLine), int(r.prevColumn)) | ||||
| } | ||||
| 
 | ||||
| func (r *importReader) posv0() { | ||||
| 	delta := r.int64() | ||||
| 	if delta != deltaNewFile { | ||||
| 		r.prevLine += delta | ||||
| 	} else if l := r.int64(); l == -1 { | ||||
| 		r.prevLine += deltaNewFile | ||||
| 	} else { | ||||
| 		r.prevFile = r.string() | ||||
| 		r.prevLine = l | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (r *importReader) posv1() { | ||||
| 	delta := r.int64() | ||||
| 	r.prevColumn += delta >> 1 | ||||
| 	if delta&1 != 0 { | ||||
| 		delta = r.int64() | ||||
| 		r.prevLine += delta >> 1 | ||||
| 		if delta&1 != 0 { | ||||
| 			r.prevFile = r.string() | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (r *importReader) typ() types.Type { | ||||
| 	return r.p.typAt(r.uint64(), nil) | ||||
| } | ||||
| 
 | ||||
| func isInterface(t types.Type) bool { | ||||
| 	_, ok := t.(*types.Interface) | ||||
| 	return ok | ||||
| } | ||||
| 
 | ||||
| func (r *importReader) pkg() *types.Package { return r.p.pkgAt(r.uint64()) } | ||||
| func (r *importReader) string() string      { return r.p.stringAt(r.uint64()) } | ||||
| 
 | ||||
| func (r *importReader) doType(base *types.Named) (res types.Type) { | ||||
| 	k := r.kind() | ||||
| 	if debug { | ||||
| 		r.p.trace("importing type %d (base: %s)", k, base) | ||||
| 		r.p.indent++ | ||||
| 		defer func() { | ||||
| 			r.p.indent-- | ||||
| 			r.p.trace("=> %s", res) | ||||
| 		}() | ||||
| 	} | ||||
| 	switch k { | ||||
| 	default: | ||||
| 		errorf("unexpected kind tag in %q: %v", r.p.ipath, k) | ||||
| 		return nil | ||||
| 
 | ||||
| 	case definedType: | ||||
| 		pkg, name := r.qualifiedIdent() | ||||
| 		r.p.doDecl(pkg, name) | ||||
| 		return pkg.Scope().Lookup(name).(*types.TypeName).Type() | ||||
| 	case pointerType: | ||||
| 		return types.NewPointer(r.typ()) | ||||
| 	case sliceType: | ||||
| 		return types.NewSlice(r.typ()) | ||||
| 	case arrayType: | ||||
| 		n := r.uint64() | ||||
| 		return types.NewArray(r.typ(), int64(n)) | ||||
| 	case chanType: | ||||
| 		dir := chanDir(int(r.uint64())) | ||||
| 		return types.NewChan(dir, r.typ()) | ||||
| 	case mapType: | ||||
| 		return types.NewMap(r.typ(), r.typ()) | ||||
| 	case signatureType: | ||||
| 		r.currPkg = r.pkg() | ||||
| 		return r.signature(nil, nil, nil) | ||||
| 
 | ||||
| 	case structType: | ||||
| 		r.currPkg = r.pkg() | ||||
| 
 | ||||
| 		fields := make([]*types.Var, r.uint64()) | ||||
| 		tags := make([]string, len(fields)) | ||||
| 		for i := range fields { | ||||
| 			fpos := r.pos() | ||||
| 			fname := r.ident() | ||||
| 			ftyp := r.typ() | ||||
| 			emb := r.bool() | ||||
| 			tag := r.string() | ||||
| 
 | ||||
| 			fields[i] = types.NewField(fpos, r.currPkg, fname, ftyp, emb) | ||||
| 			tags[i] = tag | ||||
| 		} | ||||
| 		return types.NewStruct(fields, tags) | ||||
| 
 | ||||
| 	case interfaceType: | ||||
| 		r.currPkg = r.pkg() | ||||
| 
 | ||||
| 		embeddeds := make([]types.Type, r.uint64()) | ||||
| 		for i := range embeddeds { | ||||
| 			_ = r.pos() | ||||
| 			embeddeds[i] = r.typ() | ||||
| 		} | ||||
| 
 | ||||
| 		methods := make([]*types.Func, r.uint64()) | ||||
| 		for i := range methods { | ||||
| 			mpos := r.pos() | ||||
| 			mname := r.ident() | ||||
| 
 | ||||
| 			// TODO(mdempsky): Matches bimport.go, but I
 | ||||
| 			// don't agree with this.
 | ||||
| 			var recv *types.Var | ||||
| 			if base != nil { | ||||
| 				recv = types.NewVar(token.NoPos, r.currPkg, "", base) | ||||
| 			} | ||||
| 
 | ||||
| 			msig := r.signature(recv, nil, nil) | ||||
| 			methods[i] = types.NewFunc(mpos, r.currPkg, mname, msig) | ||||
| 		} | ||||
| 
 | ||||
| 		typ := newInterface(methods, embeddeds) | ||||
| 		r.p.interfaceList = append(r.p.interfaceList, typ) | ||||
| 		return typ | ||||
| 
 | ||||
| 	case typeParamType: | ||||
| 		if r.p.version < iexportVersionGenerics { | ||||
| 			errorf("unexpected type param type") | ||||
| 		} | ||||
| 		pkg, name := r.qualifiedIdent() | ||||
| 		id := ident{pkg, name} | ||||
| 		if t, ok := r.p.tparamIndex[id]; ok { | ||||
| 			// We're already in the process of importing this typeparam.
 | ||||
| 			return t | ||||
| 		} | ||||
| 		// Otherwise, import the definition of the typeparam now.
 | ||||
| 		r.p.doDecl(pkg, name) | ||||
| 		return r.p.tparamIndex[id] | ||||
| 
 | ||||
| 	case instanceType: | ||||
| 		if r.p.version < iexportVersionGenerics { | ||||
| 			errorf("unexpected instantiation type") | ||||
| 		} | ||||
| 		// pos does not matter for instances: they are positioned on the original
 | ||||
| 		// type.
 | ||||
| 		_ = r.pos() | ||||
| 		len := r.uint64() | ||||
| 		targs := make([]types.Type, len) | ||||
| 		for i := range targs { | ||||
| 			targs[i] = r.typ() | ||||
| 		} | ||||
| 		baseType := r.typ() | ||||
| 		// The imported instantiated type doesn't include any methods, so
 | ||||
| 		// we must always use the methods of the base (orig) type.
 | ||||
| 		// TODO provide a non-nil *Environment
 | ||||
| 		t, _ := typeparams.Instantiate(nil, baseType, targs, false) | ||||
| 		return t | ||||
| 
 | ||||
| 	case unionType: | ||||
| 		if r.p.version < iexportVersionGenerics { | ||||
| 			errorf("unexpected instantiation type") | ||||
| 		} | ||||
| 		terms := make([]*typeparams.Term, r.uint64()) | ||||
| 		for i := range terms { | ||||
| 			terms[i] = typeparams.NewTerm(r.bool(), r.typ()) | ||||
| 		} | ||||
| 		return typeparams.NewUnion(terms) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (r *importReader) kind() itag { | ||||
| 	return itag(r.uint64()) | ||||
| } | ||||
| 
 | ||||
| func (r *importReader) signature(recv *types.Var, rparams []*typeparams.TypeParam, tparams []*typeparams.TypeParam) *types.Signature { | ||||
| 	params := r.paramList() | ||||
| 	results := r.paramList() | ||||
| 	variadic := params.Len() > 0 && r.bool() | ||||
| 	return typeparams.NewSignatureType(recv, rparams, tparams, params, results, variadic) | ||||
| } | ||||
| 
 | ||||
| func (r *importReader) tparamList() []*typeparams.TypeParam { | ||||
| 	n := r.uint64() | ||||
| 	if n == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	xs := make([]*typeparams.TypeParam, n) | ||||
| 	for i := range xs { | ||||
| 		// Note: the standard library importer is tolerant of nil types here,
 | ||||
| 		// though would panic in SetTypeParams.
 | ||||
| 		xs[i] = r.typ().(*typeparams.TypeParam) | ||||
| 	} | ||||
| 	return xs | ||||
| } | ||||
| 
 | ||||
| func (r *importReader) paramList() *types.Tuple { | ||||
| 	xs := make([]*types.Var, r.uint64()) | ||||
| 	for i := range xs { | ||||
| 		xs[i] = r.param() | ||||
| 	} | ||||
| 	return types.NewTuple(xs...) | ||||
| } | ||||
| 
 | ||||
| func (r *importReader) param() *types.Var { | ||||
| 	pos := r.pos() | ||||
| 	name := r.ident() | ||||
| 	typ := r.typ() | ||||
| 	return types.NewParam(pos, r.currPkg, name, typ) | ||||
| } | ||||
| 
 | ||||
| func (r *importReader) bool() bool { | ||||
| 	return r.uint64() != 0 | ||||
| } | ||||
| 
 | ||||
| func (r *importReader) int64() int64 { | ||||
| 	n, err := binary.ReadVarint(&r.declReader) | ||||
| 	if err != nil { | ||||
| 		errorf("readVarint: %v", err) | ||||
| 	} | ||||
| 	return n | ||||
| } | ||||
| 
 | ||||
| func (r *importReader) uint64() uint64 { | ||||
| 	n, err := binary.ReadUvarint(&r.declReader) | ||||
| 	if err != nil { | ||||
| 		errorf("readUvarint: %v", err) | ||||
| 	} | ||||
| 	return n | ||||
| } | ||||
| 
 | ||||
| func (r *importReader) byte() byte { | ||||
| 	x, err := r.declReader.ReadByte() | ||||
| 	if err != nil { | ||||
| 		errorf("declReader.ReadByte: %v", err) | ||||
| 	} | ||||
| 	return x | ||||
| } | ||||
| 
 | ||||
| func baseType(typ types.Type) *types.Named { | ||||
| 	// pointer receivers are never types.Named types
 | ||||
| 	if p, _ := typ.(*types.Pointer); p != nil { | ||||
| 		typ = p.Elem() | ||||
| 	} | ||||
| 	// receiver base types are always (possibly generic) types.Named types
 | ||||
| 	n, _ := typ.(*types.Named) | ||||
| 	return n | ||||
| } | ||||
							
								
								
									
										22
									
								
								vendor/golang.org/x/tools/go/internal/gcimporter/newInterface10.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										22
									
								
								vendor/golang.org/x/tools/go/internal/gcimporter/newInterface10.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							|  | @ -0,0 +1,22 @@ | |||
| // Copyright 2018 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.
 | ||||
| 
 | ||||
| //go:build !go1.11
 | ||||
| // +build !go1.11
 | ||||
| 
 | ||||
| package gcimporter | ||||
| 
 | ||||
| import "go/types" | ||||
| 
 | ||||
| func newInterface(methods []*types.Func, embeddeds []types.Type) *types.Interface { | ||||
| 	named := make([]*types.Named, len(embeddeds)) | ||||
| 	for i, e := range embeddeds { | ||||
| 		var ok bool | ||||
| 		named[i], ok = e.(*types.Named) | ||||
| 		if !ok { | ||||
| 			panic("embedding of non-defined interfaces in interfaces is not supported before Go 1.11") | ||||
| 		} | ||||
| 	} | ||||
| 	return types.NewInterface(methods, named) | ||||
| } | ||||
							
								
								
									
										14
									
								
								vendor/golang.org/x/tools/go/internal/gcimporter/newInterface11.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										14
									
								
								vendor/golang.org/x/tools/go/internal/gcimporter/newInterface11.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							|  | @ -0,0 +1,14 @@ | |||
| // Copyright 2018 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.
 | ||||
| 
 | ||||
| //go:build go1.11
 | ||||
| // +build go1.11
 | ||||
| 
 | ||||
| package gcimporter | ||||
| 
 | ||||
| import "go/types" | ||||
| 
 | ||||
| func newInterface(methods []*types.Func, embeddeds []types.Type) *types.Interface { | ||||
| 	return types.NewInterfaceType(methods, embeddeds) | ||||
| } | ||||
|  | @ -0,0 +1,16 @@ | |||
| // Copyright 2021 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.
 | ||||
| 
 | ||||
| //go:build !go1.18
 | ||||
| // +build !go1.18
 | ||||
| 
 | ||||
| package gcimporter | ||||
| 
 | ||||
| import "go/types" | ||||
| 
 | ||||
| const iexportVersion = iexportVersionGo1_11 | ||||
| 
 | ||||
| func additionalPredeclared() []types.Type { | ||||
| 	return nil | ||||
| } | ||||
|  | @ -0,0 +1,23 @@ | |||
| // Copyright 2021 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.
 | ||||
| 
 | ||||
| //go:build go1.18
 | ||||
| // +build go1.18
 | ||||
| 
 | ||||
| package gcimporter | ||||
| 
 | ||||
| import "go/types" | ||||
| 
 | ||||
| const iexportVersion = iexportVersionGenerics | ||||
| 
 | ||||
| // additionalPredeclared returns additional predeclared types in go.1.18.
 | ||||
| func additionalPredeclared() []types.Type { | ||||
| 	return []types.Type{ | ||||
| 		// comparable
 | ||||
| 		types.Universe.Lookup("comparable").Type(), | ||||
| 
 | ||||
| 		// any
 | ||||
| 		types.Universe.Lookup("any").Type(), | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,10 @@ | |||
| // Copyright 2022 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.
 | ||||
| 
 | ||||
| //go:build !(go1.18 && goexperiment.unified)
 | ||||
| // +build !go1.18 !goexperiment.unified
 | ||||
| 
 | ||||
| package gcimporter | ||||
| 
 | ||||
| const unifiedIR = false | ||||
|  | @ -0,0 +1,10 @@ | |||
| // Copyright 2022 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.
 | ||||
| 
 | ||||
| //go:build go1.18 && goexperiment.unified
 | ||||
| // +build go1.18,goexperiment.unified
 | ||||
| 
 | ||||
| package gcimporter | ||||
| 
 | ||||
| const unifiedIR = true | ||||
|  | @ -0,0 +1,19 @@ | |||
| // Copyright 2022 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.
 | ||||
| 
 | ||||
| //go:build !go1.18
 | ||||
| // +build !go1.18
 | ||||
| 
 | ||||
| package gcimporter | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"go/token" | ||||
| 	"go/types" | ||||
| ) | ||||
| 
 | ||||
| func UImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) { | ||||
| 	err = fmt.Errorf("go/tools compiled with a Go version earlier than 1.18 cannot read unified IR export data") | ||||
| 	return | ||||
| } | ||||
|  | @ -0,0 +1,612 @@ | |||
| // Copyright 2021 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.
 | ||||
| 
 | ||||
| // Derived from go/internal/gcimporter/ureader.go
 | ||||
| 
 | ||||
| //go:build go1.18
 | ||||
| // +build go1.18
 | ||||
| 
 | ||||
| package gcimporter | ||||
| 
 | ||||
| import ( | ||||
| 	"go/token" | ||||
| 	"go/types" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"golang.org/x/tools/go/internal/pkgbits" | ||||
| ) | ||||
| 
 | ||||
| // A pkgReader holds the shared state for reading a unified IR package
 | ||||
| // description.
 | ||||
| type pkgReader struct { | ||||
| 	pkgbits.PkgDecoder | ||||
| 
 | ||||
| 	fake fakeFileSet | ||||
| 
 | ||||
| 	ctxt    *types.Context | ||||
| 	imports map[string]*types.Package // previously imported packages, indexed by path
 | ||||
| 
 | ||||
| 	// lazily initialized arrays corresponding to the unified IR
 | ||||
| 	// PosBase, Pkg, and Type sections, respectively.
 | ||||
| 	posBases []string // position bases (i.e., file names)
 | ||||
| 	pkgs     []*types.Package | ||||
| 	typs     []types.Type | ||||
| 
 | ||||
| 	// laterFns holds functions that need to be invoked at the end of
 | ||||
| 	// import reading.
 | ||||
| 	laterFns []func() | ||||
| } | ||||
| 
 | ||||
| // later adds a function to be invoked at the end of import reading.
 | ||||
| func (pr *pkgReader) later(fn func()) { | ||||
| 	pr.laterFns = append(pr.laterFns, fn) | ||||
| } | ||||
| 
 | ||||
| // See cmd/compile/internal/noder.derivedInfo.
 | ||||
| type derivedInfo struct { | ||||
| 	idx    pkgbits.Index | ||||
| 	needed bool | ||||
| } | ||||
| 
 | ||||
| // See cmd/compile/internal/noder.typeInfo.
 | ||||
| type typeInfo struct { | ||||
| 	idx     pkgbits.Index | ||||
| 	derived bool | ||||
| } | ||||
| 
 | ||||
| func UImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) { | ||||
| 	s := string(data) | ||||
| 	s = s[:strings.LastIndex(s, "\n$$\n")] | ||||
| 	input := pkgbits.NewPkgDecoder(path, s) | ||||
| 	pkg = readUnifiedPackage(fset, nil, imports, input) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // readUnifiedPackage reads a package description from the given
 | ||||
| // unified IR export data decoder.
 | ||||
| func readUnifiedPackage(fset *token.FileSet, ctxt *types.Context, imports map[string]*types.Package, input pkgbits.PkgDecoder) *types.Package { | ||||
| 	pr := pkgReader{ | ||||
| 		PkgDecoder: input, | ||||
| 
 | ||||
| 		fake: fakeFileSet{ | ||||
| 			fset:  fset, | ||||
| 			files: make(map[string]*fileInfo), | ||||
| 		}, | ||||
| 
 | ||||
| 		ctxt:    ctxt, | ||||
| 		imports: imports, | ||||
| 
 | ||||
| 		posBases: make([]string, input.NumElems(pkgbits.RelocPosBase)), | ||||
| 		pkgs:     make([]*types.Package, input.NumElems(pkgbits.RelocPkg)), | ||||
| 		typs:     make([]types.Type, input.NumElems(pkgbits.RelocType)), | ||||
| 	} | ||||
| 	defer pr.fake.setLines() | ||||
| 
 | ||||
| 	r := pr.newReader(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic) | ||||
| 	pkg := r.pkg() | ||||
| 	r.Bool() // has init
 | ||||
| 
 | ||||
| 	for i, n := 0, r.Len(); i < n; i++ { | ||||
| 		// As if r.obj(), but avoiding the Scope.Lookup call,
 | ||||
| 		// to avoid eager loading of imports.
 | ||||
| 		r.Sync(pkgbits.SyncObject) | ||||
| 		assert(!r.Bool()) | ||||
| 		r.p.objIdx(r.Reloc(pkgbits.RelocObj)) | ||||
| 		assert(r.Len() == 0) | ||||
| 	} | ||||
| 
 | ||||
| 	r.Sync(pkgbits.SyncEOF) | ||||
| 
 | ||||
| 	for _, fn := range pr.laterFns { | ||||
| 		fn() | ||||
| 	} | ||||
| 
 | ||||
| 	pkg.MarkComplete() | ||||
| 	return pkg | ||||
| } | ||||
| 
 | ||||
| // A reader holds the state for reading a single unified IR element
 | ||||
| // within a package.
 | ||||
| type reader struct { | ||||
| 	pkgbits.Decoder | ||||
| 
 | ||||
| 	p *pkgReader | ||||
| 
 | ||||
| 	dict *readerDict | ||||
| } | ||||
| 
 | ||||
| // A readerDict holds the state for type parameters that parameterize
 | ||||
| // the current unified IR element.
 | ||||
| type readerDict struct { | ||||
| 	// bounds is a slice of typeInfos corresponding to the underlying
 | ||||
| 	// bounds of the element's type parameters.
 | ||||
| 	bounds []typeInfo | ||||
| 
 | ||||
| 	// tparams is a slice of the constructed TypeParams for the element.
 | ||||
| 	tparams []*types.TypeParam | ||||
| 
 | ||||
| 	// devived is a slice of types derived from tparams, which may be
 | ||||
| 	// instantiated while reading the current element.
 | ||||
| 	derived      []derivedInfo | ||||
| 	derivedTypes []types.Type // lazily instantiated from derived
 | ||||
| } | ||||
| 
 | ||||
| func (pr *pkgReader) newReader(k pkgbits.RelocKind, idx pkgbits.Index, marker pkgbits.SyncMarker) *reader { | ||||
| 	return &reader{ | ||||
| 		Decoder: pr.NewDecoder(k, idx, marker), | ||||
| 		p:       pr, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // @@@ Positions
 | ||||
| 
 | ||||
| func (r *reader) pos() token.Pos { | ||||
| 	r.Sync(pkgbits.SyncPos) | ||||
| 	if !r.Bool() { | ||||
| 		return token.NoPos | ||||
| 	} | ||||
| 
 | ||||
| 	// TODO(mdempsky): Delta encoding.
 | ||||
| 	posBase := r.posBase() | ||||
| 	line := r.Uint() | ||||
| 	col := r.Uint() | ||||
| 	return r.p.fake.pos(posBase, int(line), int(col)) | ||||
| } | ||||
| 
 | ||||
| func (r *reader) posBase() string { | ||||
| 	return r.p.posBaseIdx(r.Reloc(pkgbits.RelocPosBase)) | ||||
| } | ||||
| 
 | ||||
| func (pr *pkgReader) posBaseIdx(idx pkgbits.Index) string { | ||||
| 	if b := pr.posBases[idx]; b != "" { | ||||
| 		return b | ||||
| 	} | ||||
| 
 | ||||
| 	r := pr.newReader(pkgbits.RelocPosBase, idx, pkgbits.SyncPosBase) | ||||
| 
 | ||||
| 	// Within types2, position bases have a lot more details (e.g.,
 | ||||
| 	// keeping track of where //line directives appeared exactly).
 | ||||
| 	//
 | ||||
| 	// For go/types, we just track the file name.
 | ||||
| 
 | ||||
| 	filename := r.String() | ||||
| 
 | ||||
| 	if r.Bool() { // file base
 | ||||
| 		// Was: "b = token.NewTrimmedFileBase(filename, true)"
 | ||||
| 	} else { // line base
 | ||||
| 		pos := r.pos() | ||||
| 		line := r.Uint() | ||||
| 		col := r.Uint() | ||||
| 
 | ||||
| 		// Was: "b = token.NewLineBase(pos, filename, true, line, col)"
 | ||||
| 		_, _, _ = pos, line, col | ||||
| 	} | ||||
| 
 | ||||
| 	b := filename | ||||
| 	pr.posBases[idx] = b | ||||
| 	return b | ||||
| } | ||||
| 
 | ||||
| // @@@ Packages
 | ||||
| 
 | ||||
| func (r *reader) pkg() *types.Package { | ||||
| 	r.Sync(pkgbits.SyncPkg) | ||||
| 	return r.p.pkgIdx(r.Reloc(pkgbits.RelocPkg)) | ||||
| } | ||||
| 
 | ||||
| func (pr *pkgReader) pkgIdx(idx pkgbits.Index) *types.Package { | ||||
| 	// TODO(mdempsky): Consider using some non-nil pointer to indicate
 | ||||
| 	// the universe scope, so we don't need to keep re-reading it.
 | ||||
| 	if pkg := pr.pkgs[idx]; pkg != nil { | ||||
| 		return pkg | ||||
| 	} | ||||
| 
 | ||||
| 	pkg := pr.newReader(pkgbits.RelocPkg, idx, pkgbits.SyncPkgDef).doPkg() | ||||
| 	pr.pkgs[idx] = pkg | ||||
| 	return pkg | ||||
| } | ||||
| 
 | ||||
| func (r *reader) doPkg() *types.Package { | ||||
| 	path := r.String() | ||||
| 	switch path { | ||||
| 	case "": | ||||
| 		path = r.p.PkgPath() | ||||
| 	case "builtin": | ||||
| 		return nil // universe
 | ||||
| 	case "unsafe": | ||||
| 		return types.Unsafe | ||||
| 	} | ||||
| 
 | ||||
| 	if pkg := r.p.imports[path]; pkg != nil { | ||||
| 		return pkg | ||||
| 	} | ||||
| 
 | ||||
| 	name := r.String() | ||||
| 
 | ||||
| 	pkg := types.NewPackage(path, name) | ||||
| 	r.p.imports[path] = pkg | ||||
| 
 | ||||
| 	imports := make([]*types.Package, r.Len()) | ||||
| 	for i := range imports { | ||||
| 		imports[i] = r.pkg() | ||||
| 	} | ||||
| 	pkg.SetImports(imports) | ||||
| 
 | ||||
| 	return pkg | ||||
| } | ||||
| 
 | ||||
| // @@@ Types
 | ||||
| 
 | ||||
| func (r *reader) typ() types.Type { | ||||
| 	return r.p.typIdx(r.typInfo(), r.dict) | ||||
| } | ||||
| 
 | ||||
| func (r *reader) typInfo() typeInfo { | ||||
| 	r.Sync(pkgbits.SyncType) | ||||
| 	if r.Bool() { | ||||
| 		return typeInfo{idx: pkgbits.Index(r.Len()), derived: true} | ||||
| 	} | ||||
| 	return typeInfo{idx: r.Reloc(pkgbits.RelocType), derived: false} | ||||
| } | ||||
| 
 | ||||
| func (pr *pkgReader) typIdx(info typeInfo, dict *readerDict) types.Type { | ||||
| 	idx := info.idx | ||||
| 	var where *types.Type | ||||
| 	if info.derived { | ||||
| 		where = &dict.derivedTypes[idx] | ||||
| 		idx = dict.derived[idx].idx | ||||
| 	} else { | ||||
| 		where = &pr.typs[idx] | ||||
| 	} | ||||
| 
 | ||||
| 	if typ := *where; typ != nil { | ||||
| 		return typ | ||||
| 	} | ||||
| 
 | ||||
| 	r := pr.newReader(pkgbits.RelocType, idx, pkgbits.SyncTypeIdx) | ||||
| 	r.dict = dict | ||||
| 
 | ||||
| 	typ := r.doTyp() | ||||
| 	assert(typ != nil) | ||||
| 
 | ||||
| 	// See comment in pkgReader.typIdx explaining how this happens.
 | ||||
| 	if prev := *where; prev != nil { | ||||
| 		return prev | ||||
| 	} | ||||
| 
 | ||||
| 	*where = typ | ||||
| 	return typ | ||||
| } | ||||
| 
 | ||||
| func (r *reader) doTyp() (res types.Type) { | ||||
| 	switch tag := pkgbits.CodeType(r.Code(pkgbits.SyncType)); tag { | ||||
| 	default: | ||||
| 		errorf("unhandled type tag: %v", tag) | ||||
| 		panic("unreachable") | ||||
| 
 | ||||
| 	case pkgbits.TypeBasic: | ||||
| 		return types.Typ[r.Len()] | ||||
| 
 | ||||
| 	case pkgbits.TypeNamed: | ||||
| 		obj, targs := r.obj() | ||||
| 		name := obj.(*types.TypeName) | ||||
| 		if len(targs) != 0 { | ||||
| 			t, _ := types.Instantiate(r.p.ctxt, name.Type(), targs, false) | ||||
| 			return t | ||||
| 		} | ||||
| 		return name.Type() | ||||
| 
 | ||||
| 	case pkgbits.TypeTypeParam: | ||||
| 		return r.dict.tparams[r.Len()] | ||||
| 
 | ||||
| 	case pkgbits.TypeArray: | ||||
| 		len := int64(r.Uint64()) | ||||
| 		return types.NewArray(r.typ(), len) | ||||
| 	case pkgbits.TypeChan: | ||||
| 		dir := types.ChanDir(r.Len()) | ||||
| 		return types.NewChan(dir, r.typ()) | ||||
| 	case pkgbits.TypeMap: | ||||
| 		return types.NewMap(r.typ(), r.typ()) | ||||
| 	case pkgbits.TypePointer: | ||||
| 		return types.NewPointer(r.typ()) | ||||
| 	case pkgbits.TypeSignature: | ||||
| 		return r.signature(nil, nil, nil) | ||||
| 	case pkgbits.TypeSlice: | ||||
| 		return types.NewSlice(r.typ()) | ||||
| 	case pkgbits.TypeStruct: | ||||
| 		return r.structType() | ||||
| 	case pkgbits.TypeInterface: | ||||
| 		return r.interfaceType() | ||||
| 	case pkgbits.TypeUnion: | ||||
| 		return r.unionType() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (r *reader) structType() *types.Struct { | ||||
| 	fields := make([]*types.Var, r.Len()) | ||||
| 	var tags []string | ||||
| 	for i := range fields { | ||||
| 		pos := r.pos() | ||||
| 		pkg, name := r.selector() | ||||
| 		ftyp := r.typ() | ||||
| 		tag := r.String() | ||||
| 		embedded := r.Bool() | ||||
| 
 | ||||
| 		fields[i] = types.NewField(pos, pkg, name, ftyp, embedded) | ||||
| 		if tag != "" { | ||||
| 			for len(tags) < i { | ||||
| 				tags = append(tags, "") | ||||
| 			} | ||||
| 			tags = append(tags, tag) | ||||
| 		} | ||||
| 	} | ||||
| 	return types.NewStruct(fields, tags) | ||||
| } | ||||
| 
 | ||||
| func (r *reader) unionType() *types.Union { | ||||
| 	terms := make([]*types.Term, r.Len()) | ||||
| 	for i := range terms { | ||||
| 		terms[i] = types.NewTerm(r.Bool(), r.typ()) | ||||
| 	} | ||||
| 	return types.NewUnion(terms) | ||||
| } | ||||
| 
 | ||||
| func (r *reader) interfaceType() *types.Interface { | ||||
| 	methods := make([]*types.Func, r.Len()) | ||||
| 	embeddeds := make([]types.Type, r.Len()) | ||||
| 	implicit := len(methods) == 0 && len(embeddeds) == 1 && r.Bool() | ||||
| 
 | ||||
| 	for i := range methods { | ||||
| 		pos := r.pos() | ||||
| 		pkg, name := r.selector() | ||||
| 		mtyp := r.signature(nil, nil, nil) | ||||
| 		methods[i] = types.NewFunc(pos, pkg, name, mtyp) | ||||
| 	} | ||||
| 
 | ||||
| 	for i := range embeddeds { | ||||
| 		embeddeds[i] = r.typ() | ||||
| 	} | ||||
| 
 | ||||
| 	iface := types.NewInterfaceType(methods, embeddeds) | ||||
| 	if implicit { | ||||
| 		iface.MarkImplicit() | ||||
| 	} | ||||
| 	return iface | ||||
| } | ||||
| 
 | ||||
| func (r *reader) signature(recv *types.Var, rtparams, tparams []*types.TypeParam) *types.Signature { | ||||
| 	r.Sync(pkgbits.SyncSignature) | ||||
| 
 | ||||
| 	params := r.params() | ||||
| 	results := r.params() | ||||
| 	variadic := r.Bool() | ||||
| 
 | ||||
| 	return types.NewSignatureType(recv, rtparams, tparams, params, results, variadic) | ||||
| } | ||||
| 
 | ||||
| func (r *reader) params() *types.Tuple { | ||||
| 	r.Sync(pkgbits.SyncParams) | ||||
| 
 | ||||
| 	params := make([]*types.Var, r.Len()) | ||||
| 	for i := range params { | ||||
| 		params[i] = r.param() | ||||
| 	} | ||||
| 
 | ||||
| 	return types.NewTuple(params...) | ||||
| } | ||||
| 
 | ||||
| func (r *reader) param() *types.Var { | ||||
| 	r.Sync(pkgbits.SyncParam) | ||||
| 
 | ||||
| 	pos := r.pos() | ||||
| 	pkg, name := r.localIdent() | ||||
| 	typ := r.typ() | ||||
| 
 | ||||
| 	return types.NewParam(pos, pkg, name, typ) | ||||
| } | ||||
| 
 | ||||
| // @@@ Objects
 | ||||
| 
 | ||||
| func (r *reader) obj() (types.Object, []types.Type) { | ||||
| 	r.Sync(pkgbits.SyncObject) | ||||
| 
 | ||||
| 	assert(!r.Bool()) | ||||
| 
 | ||||
| 	pkg, name := r.p.objIdx(r.Reloc(pkgbits.RelocObj)) | ||||
| 	obj := pkgScope(pkg).Lookup(name) | ||||
| 
 | ||||
| 	targs := make([]types.Type, r.Len()) | ||||
| 	for i := range targs { | ||||
| 		targs[i] = r.typ() | ||||
| 	} | ||||
| 
 | ||||
| 	return obj, targs | ||||
| } | ||||
| 
 | ||||
| func (pr *pkgReader) objIdx(idx pkgbits.Index) (*types.Package, string) { | ||||
| 	rname := pr.newReader(pkgbits.RelocName, idx, pkgbits.SyncObject1) | ||||
| 
 | ||||
| 	objPkg, objName := rname.qualifiedIdent() | ||||
| 	assert(objName != "") | ||||
| 
 | ||||
| 	tag := pkgbits.CodeObj(rname.Code(pkgbits.SyncCodeObj)) | ||||
| 
 | ||||
| 	if tag == pkgbits.ObjStub { | ||||
| 		assert(objPkg == nil || objPkg == types.Unsafe) | ||||
| 		return objPkg, objName | ||||
| 	} | ||||
| 
 | ||||
| 	if objPkg.Scope().Lookup(objName) == nil { | ||||
| 		dict := pr.objDictIdx(idx) | ||||
| 
 | ||||
| 		r := pr.newReader(pkgbits.RelocObj, idx, pkgbits.SyncObject1) | ||||
| 		r.dict = dict | ||||
| 
 | ||||
| 		declare := func(obj types.Object) { | ||||
| 			objPkg.Scope().Insert(obj) | ||||
| 		} | ||||
| 
 | ||||
| 		switch tag { | ||||
| 		default: | ||||
| 			panic("weird") | ||||
| 
 | ||||
| 		case pkgbits.ObjAlias: | ||||
| 			pos := r.pos() | ||||
| 			typ := r.typ() | ||||
| 			declare(types.NewTypeName(pos, objPkg, objName, typ)) | ||||
| 
 | ||||
| 		case pkgbits.ObjConst: | ||||
| 			pos := r.pos() | ||||
| 			typ := r.typ() | ||||
| 			val := r.Value() | ||||
| 			declare(types.NewConst(pos, objPkg, objName, typ, val)) | ||||
| 
 | ||||
| 		case pkgbits.ObjFunc: | ||||
| 			pos := r.pos() | ||||
| 			tparams := r.typeParamNames() | ||||
| 			sig := r.signature(nil, nil, tparams) | ||||
| 			declare(types.NewFunc(pos, objPkg, objName, sig)) | ||||
| 
 | ||||
| 		case pkgbits.ObjType: | ||||
| 			pos := r.pos() | ||||
| 
 | ||||
| 			obj := types.NewTypeName(pos, objPkg, objName, nil) | ||||
| 			named := types.NewNamed(obj, nil, nil) | ||||
| 			declare(obj) | ||||
| 
 | ||||
| 			named.SetTypeParams(r.typeParamNames()) | ||||
| 
 | ||||
| 			// TODO(mdempsky): Rewrite receiver types to underlying is an
 | ||||
| 			// Interface? The go/types importer does this (I think because
 | ||||
| 			// unit tests expected that), but cmd/compile doesn't care
 | ||||
| 			// about it, so maybe we can avoid worrying about that here.
 | ||||
| 			rhs := r.typ() | ||||
| 			r.p.later(func() { | ||||
| 				underlying := rhs.Underlying() | ||||
| 				named.SetUnderlying(underlying) | ||||
| 			}) | ||||
| 
 | ||||
| 			for i, n := 0, r.Len(); i < n; i++ { | ||||
| 				named.AddMethod(r.method()) | ||||
| 			} | ||||
| 
 | ||||
| 		case pkgbits.ObjVar: | ||||
| 			pos := r.pos() | ||||
| 			typ := r.typ() | ||||
| 			declare(types.NewVar(pos, objPkg, objName, typ)) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return objPkg, objName | ||||
| } | ||||
| 
 | ||||
| func (pr *pkgReader) objDictIdx(idx pkgbits.Index) *readerDict { | ||||
| 	r := pr.newReader(pkgbits.RelocObjDict, idx, pkgbits.SyncObject1) | ||||
| 
 | ||||
| 	var dict readerDict | ||||
| 
 | ||||
| 	if implicits := r.Len(); implicits != 0 { | ||||
| 		errorf("unexpected object with %v implicit type parameter(s)", implicits) | ||||
| 	} | ||||
| 
 | ||||
| 	dict.bounds = make([]typeInfo, r.Len()) | ||||
| 	for i := range dict.bounds { | ||||
| 		dict.bounds[i] = r.typInfo() | ||||
| 	} | ||||
| 
 | ||||
| 	dict.derived = make([]derivedInfo, r.Len()) | ||||
| 	dict.derivedTypes = make([]types.Type, len(dict.derived)) | ||||
| 	for i := range dict.derived { | ||||
| 		dict.derived[i] = derivedInfo{r.Reloc(pkgbits.RelocType), r.Bool()} | ||||
| 	} | ||||
| 
 | ||||
| 	// function references follow, but reader doesn't need those
 | ||||
| 
 | ||||
| 	return &dict | ||||
| } | ||||
| 
 | ||||
| func (r *reader) typeParamNames() []*types.TypeParam { | ||||
| 	r.Sync(pkgbits.SyncTypeParamNames) | ||||
| 
 | ||||
| 	// Note: This code assumes it only processes objects without
 | ||||
| 	// implement type parameters. This is currently fine, because
 | ||||
| 	// reader is only used to read in exported declarations, which are
 | ||||
| 	// always package scoped.
 | ||||
| 
 | ||||
| 	if len(r.dict.bounds) == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	// Careful: Type parameter lists may have cycles. To allow for this,
 | ||||
| 	// we construct the type parameter list in two passes: first we
 | ||||
| 	// create all the TypeNames and TypeParams, then we construct and
 | ||||
| 	// set the bound type.
 | ||||
| 
 | ||||
| 	r.dict.tparams = make([]*types.TypeParam, len(r.dict.bounds)) | ||||
| 	for i := range r.dict.bounds { | ||||
| 		pos := r.pos() | ||||
| 		pkg, name := r.localIdent() | ||||
| 
 | ||||
| 		tname := types.NewTypeName(pos, pkg, name, nil) | ||||
| 		r.dict.tparams[i] = types.NewTypeParam(tname, nil) | ||||
| 	} | ||||
| 
 | ||||
| 	typs := make([]types.Type, len(r.dict.bounds)) | ||||
| 	for i, bound := range r.dict.bounds { | ||||
| 		typs[i] = r.p.typIdx(bound, r.dict) | ||||
| 	} | ||||
| 
 | ||||
| 	// TODO(mdempsky): This is subtle, elaborate further.
 | ||||
| 	//
 | ||||
| 	// We have to save tparams outside of the closure, because
 | ||||
| 	// typeParamNames() can be called multiple times with the same
 | ||||
| 	// dictionary instance.
 | ||||
| 	//
 | ||||
| 	// Also, this needs to happen later to make sure SetUnderlying has
 | ||||
| 	// been called.
 | ||||
| 	//
 | ||||
| 	// TODO(mdempsky): Is it safe to have a single "later" slice or do
 | ||||
| 	// we need to have multiple passes? See comments on CL 386002 and
 | ||||
| 	// go.dev/issue/52104.
 | ||||
| 	tparams := r.dict.tparams | ||||
| 	r.p.later(func() { | ||||
| 		for i, typ := range typs { | ||||
| 			tparams[i].SetConstraint(typ) | ||||
| 		} | ||||
| 	}) | ||||
| 
 | ||||
| 	return r.dict.tparams | ||||
| } | ||||
| 
 | ||||
| func (r *reader) method() *types.Func { | ||||
| 	r.Sync(pkgbits.SyncMethod) | ||||
| 	pos := r.pos() | ||||
| 	pkg, name := r.selector() | ||||
| 
 | ||||
| 	rparams := r.typeParamNames() | ||||
| 	sig := r.signature(r.param(), rparams, nil) | ||||
| 
 | ||||
| 	_ = r.pos() // TODO(mdempsky): Remove; this is a hacker for linker.go.
 | ||||
| 	return types.NewFunc(pos, pkg, name, sig) | ||||
| } | ||||
| 
 | ||||
| func (r *reader) qualifiedIdent() (*types.Package, string) { return r.ident(pkgbits.SyncSym) } | ||||
| func (r *reader) localIdent() (*types.Package, string)     { return r.ident(pkgbits.SyncLocalIdent) } | ||||
| func (r *reader) selector() (*types.Package, string)       { return r.ident(pkgbits.SyncSelector) } | ||||
| 
 | ||||
| func (r *reader) ident(marker pkgbits.SyncMarker) (*types.Package, string) { | ||||
| 	r.Sync(marker) | ||||
| 	return r.pkg(), r.String() | ||||
| } | ||||
| 
 | ||||
| // pkgScope returns pkg.Scope().
 | ||||
| // If pkg is nil, it returns types.Universe instead.
 | ||||
| //
 | ||||
| // TODO(mdempsky): Remove after x/tools can depend on Go 1.19.
 | ||||
| func pkgScope(pkg *types.Package) *types.Scope { | ||||
| 	if pkg != nil { | ||||
| 		return pkg.Scope() | ||||
| 	} | ||||
| 	return types.Universe | ||||
| } | ||||
|  | @ -0,0 +1,49 @@ | |||
| // Copyright 2018 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.
 | ||||
| 
 | ||||
| // Package packagesdriver fetches type sizes for go/packages and go/analysis.
 | ||||
| package packagesdriver | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"go/types" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"golang.org/x/tools/internal/gocommand" | ||||
| ) | ||||
| 
 | ||||
| var debug = false | ||||
| 
 | ||||
| func GetSizesGolist(ctx context.Context, inv gocommand.Invocation, gocmdRunner *gocommand.Runner) (types.Sizes, error) { | ||||
| 	inv.Verb = "list" | ||||
| 	inv.Args = []string{"-f", "{{context.GOARCH}} {{context.Compiler}}", "--", "unsafe"} | ||||
| 	stdout, stderr, friendlyErr, rawErr := gocmdRunner.RunRaw(ctx, inv) | ||||
| 	var goarch, compiler string | ||||
| 	if rawErr != nil { | ||||
| 		if rawErrMsg := rawErr.Error(); strings.Contains(rawErrMsg, "cannot find main module") || strings.Contains(rawErrMsg, "go.mod file not found") { | ||||
| 			// User's running outside of a module. All bets are off. Get GOARCH and guess compiler is gc.
 | ||||
| 			// TODO(matloob): Is this a problem in practice?
 | ||||
| 			inv.Verb = "env" | ||||
| 			inv.Args = []string{"GOARCH"} | ||||
| 			envout, enverr := gocmdRunner.Run(ctx, inv) | ||||
| 			if enverr != nil { | ||||
| 				return nil, enverr | ||||
| 			} | ||||
| 			goarch = strings.TrimSpace(envout.String()) | ||||
| 			compiler = "gc" | ||||
| 		} else { | ||||
| 			return nil, friendlyErr | ||||
| 		} | ||||
| 	} else { | ||||
| 		fields := strings.Fields(stdout.String()) | ||||
| 		if len(fields) < 2 { | ||||
| 			return nil, fmt.Errorf("could not parse GOARCH and Go compiler in format \"<GOARCH> <compiler>\":\nstdout: <<%s>>\nstderr: <<%s>>", | ||||
| 				stdout.String(), stderr.String()) | ||||
| 		} | ||||
| 		goarch = fields[0] | ||||
| 		compiler = fields[1] | ||||
| 	} | ||||
| 	return types.SizesFor(compiler, goarch), nil | ||||
| } | ||||
|  | @ -0,0 +1,77 @@ | |||
| // Copyright 2021 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.
 | ||||
| 
 | ||||
| package pkgbits | ||||
| 
 | ||||
| // A Code is an enum value that can be encoded into bitstreams.
 | ||||
| //
 | ||||
| // Code types are preferable for enum types, because they allow
 | ||||
| // Decoder to detect desyncs.
 | ||||
| type Code interface { | ||||
| 	// Marker returns the SyncMarker for the Code's dynamic type.
 | ||||
| 	Marker() SyncMarker | ||||
| 
 | ||||
| 	// Value returns the Code's ordinal value.
 | ||||
| 	Value() int | ||||
| } | ||||
| 
 | ||||
| // A CodeVal distinguishes among go/constant.Value encodings.
 | ||||
| type CodeVal int | ||||
| 
 | ||||
| func (c CodeVal) Marker() SyncMarker { return SyncVal } | ||||
| func (c CodeVal) Value() int         { return int(c) } | ||||
| 
 | ||||
| // Note: These values are public and cannot be changed without
 | ||||
| // updating the go/types importers.
 | ||||
| 
 | ||||
| const ( | ||||
| 	ValBool CodeVal = iota | ||||
| 	ValString | ||||
| 	ValInt64 | ||||
| 	ValBigInt | ||||
| 	ValBigRat | ||||
| 	ValBigFloat | ||||
| ) | ||||
| 
 | ||||
| // A CodeType distinguishes among go/types.Type encodings.
 | ||||
| type CodeType int | ||||
| 
 | ||||
| func (c CodeType) Marker() SyncMarker { return SyncType } | ||||
| func (c CodeType) Value() int         { return int(c) } | ||||
| 
 | ||||
| // Note: These values are public and cannot be changed without
 | ||||
| // updating the go/types importers.
 | ||||
| 
 | ||||
| const ( | ||||
| 	TypeBasic CodeType = iota | ||||
| 	TypeNamed | ||||
| 	TypePointer | ||||
| 	TypeSlice | ||||
| 	TypeArray | ||||
| 	TypeChan | ||||
| 	TypeMap | ||||
| 	TypeSignature | ||||
| 	TypeStruct | ||||
| 	TypeInterface | ||||
| 	TypeUnion | ||||
| 	TypeTypeParam | ||||
| ) | ||||
| 
 | ||||
| // A CodeObj distinguishes among go/types.Object encodings.
 | ||||
| type CodeObj int | ||||
| 
 | ||||
| func (c CodeObj) Marker() SyncMarker { return SyncCodeObj } | ||||
| func (c CodeObj) Value() int         { return int(c) } | ||||
| 
 | ||||
| // Note: These values are public and cannot be changed without
 | ||||
| // updating the go/types importers.
 | ||||
| 
 | ||||
| const ( | ||||
| 	ObjAlias CodeObj = iota | ||||
| 	ObjConst | ||||
| 	ObjType | ||||
| 	ObjFunc | ||||
| 	ObjVar | ||||
| 	ObjStub | ||||
| ) | ||||
|  | @ -0,0 +1,433 @@ | |||
| // Copyright 2021 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.
 | ||||
| 
 | ||||
| package pkgbits | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/binary" | ||||
| 	"fmt" | ||||
| 	"go/constant" | ||||
| 	"go/token" | ||||
| 	"math/big" | ||||
| 	"os" | ||||
| 	"runtime" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // A PkgDecoder provides methods for decoding a package's Unified IR
 | ||||
| // export data.
 | ||||
| type PkgDecoder struct { | ||||
| 	// version is the file format version.
 | ||||
| 	version uint32 | ||||
| 
 | ||||
| 	// sync indicates whether the file uses sync markers.
 | ||||
| 	sync bool | ||||
| 
 | ||||
| 	// pkgPath is the package path for the package to be decoded.
 | ||||
| 	//
 | ||||
| 	// TODO(mdempsky): Remove; unneeded since CL 391014.
 | ||||
| 	pkgPath string | ||||
| 
 | ||||
| 	// elemData is the full data payload of the encoded package.
 | ||||
| 	// Elements are densely and contiguously packed together.
 | ||||
| 	//
 | ||||
| 	// The last 8 bytes of elemData are the package fingerprint.
 | ||||
| 	elemData string | ||||
| 
 | ||||
| 	// elemEnds stores the byte-offset end positions of element
 | ||||
| 	// bitstreams within elemData.
 | ||||
| 	//
 | ||||
| 	// For example, element I's bitstream data starts at elemEnds[I-1]
 | ||||
| 	// (or 0, if I==0) and ends at elemEnds[I].
 | ||||
| 	//
 | ||||
| 	// Note: elemEnds is indexed by absolute indices, not
 | ||||
| 	// section-relative indices.
 | ||||
| 	elemEnds []uint32 | ||||
| 
 | ||||
| 	// elemEndsEnds stores the index-offset end positions of relocation
 | ||||
| 	// sections within elemEnds.
 | ||||
| 	//
 | ||||
| 	// For example, section K's end positions start at elemEndsEnds[K-1]
 | ||||
| 	// (or 0, if K==0) and end at elemEndsEnds[K].
 | ||||
| 	elemEndsEnds [numRelocs]uint32 | ||||
| } | ||||
| 
 | ||||
| // PkgPath returns the package path for the package
 | ||||
| //
 | ||||
| // TODO(mdempsky): Remove; unneeded since CL 391014.
 | ||||
| func (pr *PkgDecoder) PkgPath() string { return pr.pkgPath } | ||||
| 
 | ||||
| // SyncMarkers reports whether pr uses sync markers.
 | ||||
| func (pr *PkgDecoder) SyncMarkers() bool { return pr.sync } | ||||
| 
 | ||||
| // NewPkgDecoder returns a PkgDecoder initialized to read the Unified
 | ||||
| // IR export data from input. pkgPath is the package path for the
 | ||||
| // compilation unit that produced the export data.
 | ||||
| //
 | ||||
| // TODO(mdempsky): Remove pkgPath parameter; unneeded since CL 391014.
 | ||||
| func NewPkgDecoder(pkgPath, input string) PkgDecoder { | ||||
| 	pr := PkgDecoder{ | ||||
| 		pkgPath: pkgPath, | ||||
| 	} | ||||
| 
 | ||||
| 	// TODO(mdempsky): Implement direct indexing of input string to
 | ||||
| 	// avoid copying the position information.
 | ||||
| 
 | ||||
| 	r := strings.NewReader(input) | ||||
| 
 | ||||
| 	assert(binary.Read(r, binary.LittleEndian, &pr.version) == nil) | ||||
| 
 | ||||
| 	switch pr.version { | ||||
| 	default: | ||||
| 		panic(fmt.Errorf("unsupported version: %v", pr.version)) | ||||
| 	case 0: | ||||
| 		// no flags
 | ||||
| 	case 1: | ||||
| 		var flags uint32 | ||||
| 		assert(binary.Read(r, binary.LittleEndian, &flags) == nil) | ||||
| 		pr.sync = flags&flagSyncMarkers != 0 | ||||
| 	} | ||||
| 
 | ||||
| 	assert(binary.Read(r, binary.LittleEndian, pr.elemEndsEnds[:]) == nil) | ||||
| 
 | ||||
| 	pr.elemEnds = make([]uint32, pr.elemEndsEnds[len(pr.elemEndsEnds)-1]) | ||||
| 	assert(binary.Read(r, binary.LittleEndian, pr.elemEnds[:]) == nil) | ||||
| 
 | ||||
| 	pos, err := r.Seek(0, os.SEEK_CUR) | ||||
| 	assert(err == nil) | ||||
| 
 | ||||
| 	pr.elemData = input[pos:] | ||||
| 	assert(len(pr.elemData)-8 == int(pr.elemEnds[len(pr.elemEnds)-1])) | ||||
| 
 | ||||
| 	return pr | ||||
| } | ||||
| 
 | ||||
| // NumElems returns the number of elements in section k.
 | ||||
| func (pr *PkgDecoder) NumElems(k RelocKind) int { | ||||
| 	count := int(pr.elemEndsEnds[k]) | ||||
| 	if k > 0 { | ||||
| 		count -= int(pr.elemEndsEnds[k-1]) | ||||
| 	} | ||||
| 	return count | ||||
| } | ||||
| 
 | ||||
| // TotalElems returns the total number of elements across all sections.
 | ||||
| func (pr *PkgDecoder) TotalElems() int { | ||||
| 	return len(pr.elemEnds) | ||||
| } | ||||
| 
 | ||||
| // Fingerprint returns the package fingerprint.
 | ||||
| func (pr *PkgDecoder) Fingerprint() [8]byte { | ||||
| 	var fp [8]byte | ||||
| 	copy(fp[:], pr.elemData[len(pr.elemData)-8:]) | ||||
| 	return fp | ||||
| } | ||||
| 
 | ||||
| // AbsIdx returns the absolute index for the given (section, index)
 | ||||
| // pair.
 | ||||
| func (pr *PkgDecoder) AbsIdx(k RelocKind, idx Index) int { | ||||
| 	absIdx := int(idx) | ||||
| 	if k > 0 { | ||||
| 		absIdx += int(pr.elemEndsEnds[k-1]) | ||||
| 	} | ||||
| 	if absIdx >= int(pr.elemEndsEnds[k]) { | ||||
| 		errorf("%v:%v is out of bounds; %v", k, idx, pr.elemEndsEnds) | ||||
| 	} | ||||
| 	return absIdx | ||||
| } | ||||
| 
 | ||||
| // DataIdx returns the raw element bitstream for the given (section,
 | ||||
| // index) pair.
 | ||||
| func (pr *PkgDecoder) DataIdx(k RelocKind, idx Index) string { | ||||
| 	absIdx := pr.AbsIdx(k, idx) | ||||
| 
 | ||||
| 	var start uint32 | ||||
| 	if absIdx > 0 { | ||||
| 		start = pr.elemEnds[absIdx-1] | ||||
| 	} | ||||
| 	end := pr.elemEnds[absIdx] | ||||
| 
 | ||||
| 	return pr.elemData[start:end] | ||||
| } | ||||
| 
 | ||||
| // StringIdx returns the string value for the given string index.
 | ||||
| func (pr *PkgDecoder) StringIdx(idx Index) string { | ||||
| 	return pr.DataIdx(RelocString, idx) | ||||
| } | ||||
| 
 | ||||
| // NewDecoder returns a Decoder for the given (section, index) pair,
 | ||||
| // and decodes the given SyncMarker from the element bitstream.
 | ||||
| func (pr *PkgDecoder) NewDecoder(k RelocKind, idx Index, marker SyncMarker) Decoder { | ||||
| 	r := pr.NewDecoderRaw(k, idx) | ||||
| 	r.Sync(marker) | ||||
| 	return r | ||||
| } | ||||
| 
 | ||||
| // NewDecoderRaw returns a Decoder for the given (section, index) pair.
 | ||||
| //
 | ||||
| // Most callers should use NewDecoder instead.
 | ||||
| func (pr *PkgDecoder) NewDecoderRaw(k RelocKind, idx Index) Decoder { | ||||
| 	r := Decoder{ | ||||
| 		common: pr, | ||||
| 		k:      k, | ||||
| 		Idx:    idx, | ||||
| 	} | ||||
| 
 | ||||
| 	// TODO(mdempsky) r.data.Reset(...) after #44505 is resolved.
 | ||||
| 	r.Data = *strings.NewReader(pr.DataIdx(k, idx)) | ||||
| 
 | ||||
| 	r.Sync(SyncRelocs) | ||||
| 	r.Relocs = make([]RelocEnt, r.Len()) | ||||
| 	for i := range r.Relocs { | ||||
| 		r.Sync(SyncReloc) | ||||
| 		r.Relocs[i] = RelocEnt{RelocKind(r.Len()), Index(r.Len())} | ||||
| 	} | ||||
| 
 | ||||
| 	return r | ||||
| } | ||||
| 
 | ||||
| // A Decoder provides methods for decoding an individual element's
 | ||||
| // bitstream data.
 | ||||
| type Decoder struct { | ||||
| 	common *PkgDecoder | ||||
| 
 | ||||
| 	Relocs []RelocEnt | ||||
| 	Data   strings.Reader | ||||
| 
 | ||||
| 	k   RelocKind | ||||
| 	Idx Index | ||||
| } | ||||
| 
 | ||||
| func (r *Decoder) checkErr(err error) { | ||||
| 	if err != nil { | ||||
| 		errorf("unexpected decoding error: %w", err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (r *Decoder) rawUvarint() uint64 { | ||||
| 	x, err := binary.ReadUvarint(&r.Data) | ||||
| 	r.checkErr(err) | ||||
| 	return x | ||||
| } | ||||
| 
 | ||||
| func (r *Decoder) rawVarint() int64 { | ||||
| 	ux := r.rawUvarint() | ||||
| 
 | ||||
| 	// Zig-zag decode.
 | ||||
| 	x := int64(ux >> 1) | ||||
| 	if ux&1 != 0 { | ||||
| 		x = ^x | ||||
| 	} | ||||
| 	return x | ||||
| } | ||||
| 
 | ||||
| func (r *Decoder) rawReloc(k RelocKind, idx int) Index { | ||||
| 	e := r.Relocs[idx] | ||||
| 	assert(e.Kind == k) | ||||
| 	return e.Idx | ||||
| } | ||||
| 
 | ||||
| // Sync decodes a sync marker from the element bitstream and asserts
 | ||||
| // that it matches the expected marker.
 | ||||
| //
 | ||||
| // If r.common.sync is false, then Sync is a no-op.
 | ||||
| func (r *Decoder) Sync(mWant SyncMarker) { | ||||
| 	if !r.common.sync { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	pos, _ := r.Data.Seek(0, os.SEEK_CUR) // TODO(mdempsky): io.SeekCurrent after #44505 is resolved
 | ||||
| 	mHave := SyncMarker(r.rawUvarint()) | ||||
| 	writerPCs := make([]int, r.rawUvarint()) | ||||
| 	for i := range writerPCs { | ||||
| 		writerPCs[i] = int(r.rawUvarint()) | ||||
| 	} | ||||
| 
 | ||||
| 	if mHave == mWant { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// There's some tension here between printing:
 | ||||
| 	//
 | ||||
| 	// (1) full file paths that tools can recognize (e.g., so emacs
 | ||||
| 	//     hyperlinks the "file:line" text for easy navigation), or
 | ||||
| 	//
 | ||||
| 	// (2) short file paths that are easier for humans to read (e.g., by
 | ||||
| 	//     omitting redundant or irrelevant details, so it's easier to
 | ||||
| 	//     focus on the useful bits that remain).
 | ||||
| 	//
 | ||||
| 	// The current formatting favors the former, as it seems more
 | ||||
| 	// helpful in practice. But perhaps the formatting could be improved
 | ||||
| 	// to better address both concerns. For example, use relative file
 | ||||
| 	// paths if they would be shorter, or rewrite file paths to contain
 | ||||
| 	// "$GOROOT" (like objabi.AbsFile does) if tools can be taught how
 | ||||
| 	// to reliably expand that again.
 | ||||
| 
 | ||||
| 	fmt.Printf("export data desync: package %q, section %v, index %v, offset %v\n", r.common.pkgPath, r.k, r.Idx, pos) | ||||
| 
 | ||||
| 	fmt.Printf("\nfound %v, written at:\n", mHave) | ||||
| 	if len(writerPCs) == 0 { | ||||
| 		fmt.Printf("\t[stack trace unavailable; recompile package %q with -d=syncframes]\n", r.common.pkgPath) | ||||
| 	} | ||||
| 	for _, pc := range writerPCs { | ||||
| 		fmt.Printf("\t%s\n", r.common.StringIdx(r.rawReloc(RelocString, pc))) | ||||
| 	} | ||||
| 
 | ||||
| 	fmt.Printf("\nexpected %v, reading at:\n", mWant) | ||||
| 	var readerPCs [32]uintptr // TODO(mdempsky): Dynamically size?
 | ||||
| 	n := runtime.Callers(2, readerPCs[:]) | ||||
| 	for _, pc := range fmtFrames(readerPCs[:n]...) { | ||||
| 		fmt.Printf("\t%s\n", pc) | ||||
| 	} | ||||
| 
 | ||||
| 	// We already printed a stack trace for the reader, so now we can
 | ||||
| 	// simply exit. Printing a second one with panic or base.Fatalf
 | ||||
| 	// would just be noise.
 | ||||
| 	os.Exit(1) | ||||
| } | ||||
| 
 | ||||
| // Bool decodes and returns a bool value from the element bitstream.
 | ||||
| func (r *Decoder) Bool() bool { | ||||
| 	r.Sync(SyncBool) | ||||
| 	x, err := r.Data.ReadByte() | ||||
| 	r.checkErr(err) | ||||
| 	assert(x < 2) | ||||
| 	return x != 0 | ||||
| } | ||||
| 
 | ||||
| // Int64 decodes and returns an int64 value from the element bitstream.
 | ||||
| func (r *Decoder) Int64() int64 { | ||||
| 	r.Sync(SyncInt64) | ||||
| 	return r.rawVarint() | ||||
| } | ||||
| 
 | ||||
| // Int64 decodes and returns a uint64 value from the element bitstream.
 | ||||
| func (r *Decoder) Uint64() uint64 { | ||||
| 	r.Sync(SyncUint64) | ||||
| 	return r.rawUvarint() | ||||
| } | ||||
| 
 | ||||
| // Len decodes and returns a non-negative int value from the element bitstream.
 | ||||
| func (r *Decoder) Len() int { x := r.Uint64(); v := int(x); assert(uint64(v) == x); return v } | ||||
| 
 | ||||
| // Int decodes and returns an int value from the element bitstream.
 | ||||
| func (r *Decoder) Int() int { x := r.Int64(); v := int(x); assert(int64(v) == x); return v } | ||||
| 
 | ||||
| // Uint decodes and returns a uint value from the element bitstream.
 | ||||
| func (r *Decoder) Uint() uint { x := r.Uint64(); v := uint(x); assert(uint64(v) == x); return v } | ||||
| 
 | ||||
| // Code decodes a Code value from the element bitstream and returns
 | ||||
| // its ordinal value. It's the caller's responsibility to convert the
 | ||||
| // result to an appropriate Code type.
 | ||||
| //
 | ||||
| // TODO(mdempsky): Ideally this method would have signature "Code[T
 | ||||
| // Code] T" instead, but we don't allow generic methods and the
 | ||||
| // compiler can't depend on generics yet anyway.
 | ||||
| func (r *Decoder) Code(mark SyncMarker) int { | ||||
| 	r.Sync(mark) | ||||
| 	return r.Len() | ||||
| } | ||||
| 
 | ||||
| // Reloc decodes a relocation of expected section k from the element
 | ||||
| // bitstream and returns an index to the referenced element.
 | ||||
| func (r *Decoder) Reloc(k RelocKind) Index { | ||||
| 	r.Sync(SyncUseReloc) | ||||
| 	return r.rawReloc(k, r.Len()) | ||||
| } | ||||
| 
 | ||||
| // String decodes and returns a string value from the element
 | ||||
| // bitstream.
 | ||||
| func (r *Decoder) String() string { | ||||
| 	r.Sync(SyncString) | ||||
| 	return r.common.StringIdx(r.Reloc(RelocString)) | ||||
| } | ||||
| 
 | ||||
| // Strings decodes and returns a variable-length slice of strings from
 | ||||
| // the element bitstream.
 | ||||
| func (r *Decoder) Strings() []string { | ||||
| 	res := make([]string, r.Len()) | ||||
| 	for i := range res { | ||||
| 		res[i] = r.String() | ||||
| 	} | ||||
| 	return res | ||||
| } | ||||
| 
 | ||||
| // Value decodes and returns a constant.Value from the element
 | ||||
| // bitstream.
 | ||||
| func (r *Decoder) Value() constant.Value { | ||||
| 	r.Sync(SyncValue) | ||||
| 	isComplex := r.Bool() | ||||
| 	val := r.scalar() | ||||
| 	if isComplex { | ||||
| 		val = constant.BinaryOp(val, token.ADD, constant.MakeImag(r.scalar())) | ||||
| 	} | ||||
| 	return val | ||||
| } | ||||
| 
 | ||||
| func (r *Decoder) scalar() constant.Value { | ||||
| 	switch tag := CodeVal(r.Code(SyncVal)); tag { | ||||
| 	default: | ||||
| 		panic(fmt.Errorf("unexpected scalar tag: %v", tag)) | ||||
| 
 | ||||
| 	case ValBool: | ||||
| 		return constant.MakeBool(r.Bool()) | ||||
| 	case ValString: | ||||
| 		return constant.MakeString(r.String()) | ||||
| 	case ValInt64: | ||||
| 		return constant.MakeInt64(r.Int64()) | ||||
| 	case ValBigInt: | ||||
| 		return constant.Make(r.bigInt()) | ||||
| 	case ValBigRat: | ||||
| 		num := r.bigInt() | ||||
| 		denom := r.bigInt() | ||||
| 		return constant.Make(new(big.Rat).SetFrac(num, denom)) | ||||
| 	case ValBigFloat: | ||||
| 		return constant.Make(r.bigFloat()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (r *Decoder) bigInt() *big.Int { | ||||
| 	v := new(big.Int).SetBytes([]byte(r.String())) | ||||
| 	if r.Bool() { | ||||
| 		v.Neg(v) | ||||
| 	} | ||||
| 	return v | ||||
| } | ||||
| 
 | ||||
| func (r *Decoder) bigFloat() *big.Float { | ||||
| 	v := new(big.Float).SetPrec(512) | ||||
| 	assert(v.UnmarshalText([]byte(r.String())) == nil) | ||||
| 	return v | ||||
| } | ||||
| 
 | ||||
| // @@@ Helpers
 | ||||
| 
 | ||||
| // TODO(mdempsky): These should probably be removed. I think they're a
 | ||||
| // smell that the export data format is not yet quite right.
 | ||||
| 
 | ||||
| // PeekPkgPath returns the package path for the specified package
 | ||||
| // index.
 | ||||
| func (pr *PkgDecoder) PeekPkgPath(idx Index) string { | ||||
| 	r := pr.NewDecoder(RelocPkg, idx, SyncPkgDef) | ||||
| 	path := r.String() | ||||
| 	if path == "" { | ||||
| 		path = pr.pkgPath | ||||
| 	} | ||||
| 	return path | ||||
| } | ||||
| 
 | ||||
| // PeekObj returns the package path, object name, and CodeObj for the
 | ||||
| // specified object index.
 | ||||
| func (pr *PkgDecoder) PeekObj(idx Index) (string, string, CodeObj) { | ||||
| 	r := pr.NewDecoder(RelocName, idx, SyncObject1) | ||||
| 	r.Sync(SyncSym) | ||||
| 	r.Sync(SyncPkg) | ||||
| 	path := pr.PeekPkgPath(r.Reloc(RelocPkg)) | ||||
| 	name := r.String() | ||||
| 	assert(name != "") | ||||
| 
 | ||||
| 	tag := CodeObj(r.Code(SyncCodeObj)) | ||||
| 
 | ||||
| 	return path, name, tag | ||||
| } | ||||
|  | @ -0,0 +1,32 @@ | |||
| // Copyright 2022 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.
 | ||||
| 
 | ||||
| // Package pkgbits implements low-level coding abstractions for
 | ||||
| // Unified IR's export data format.
 | ||||
| //
 | ||||
| // At a low-level, a package is a collection of bitstream elements.
 | ||||
| // Each element has a "kind" and a dense, non-negative index.
 | ||||
| // Elements can be randomly accessed given their kind and index.
 | ||||
| //
 | ||||
| // Individual elements are sequences of variable-length values (e.g.,
 | ||||
| // integers, booleans, strings, go/constant values, cross-references
 | ||||
| // to other elements). Package pkgbits provides APIs for encoding and
 | ||||
| // decoding these low-level values, but the details of mapping
 | ||||
| // higher-level Go constructs into elements is left to higher-level
 | ||||
| // abstractions.
 | ||||
| //
 | ||||
| // Elements may cross-reference each other with "relocations." For
 | ||||
| // example, an element representing a pointer type has a relocation
 | ||||
| // referring to the element type.
 | ||||
| //
 | ||||
| // Go constructs may be composed as a constellation of multiple
 | ||||
| // elements. For example, a declared function may have one element to
 | ||||
| // describe the object (e.g., its name, type, position), and a
 | ||||
| // separate element to describe its function body. This allows readers
 | ||||
| // some flexibility in efficiently seeking or re-reading data (e.g.,
 | ||||
| // inlining requires re-reading the function body for each inlined
 | ||||
| // call, without needing to re-read the object-level details).
 | ||||
| //
 | ||||
| // This is a copy of internal/pkgbits in the Go implementation.
 | ||||
| package pkgbits | ||||
|  | @ -0,0 +1,379 @@ | |||
| // Copyright 2021 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.
 | ||||
| 
 | ||||
| package pkgbits | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"crypto/md5" | ||||
| 	"encoding/binary" | ||||
| 	"go/constant" | ||||
| 	"io" | ||||
| 	"math/big" | ||||
| 	"runtime" | ||||
| ) | ||||
| 
 | ||||
| // currentVersion is the current version number.
 | ||||
| //
 | ||||
| //   - v0: initial prototype
 | ||||
| //
 | ||||
| //   - v1: adds the flags uint32 word
 | ||||
| const currentVersion uint32 = 1 | ||||
| 
 | ||||
| // A PkgEncoder provides methods for encoding a package's Unified IR
 | ||||
| // export data.
 | ||||
| type PkgEncoder struct { | ||||
| 	// elems holds the bitstream for previously encoded elements.
 | ||||
| 	elems [numRelocs][]string | ||||
| 
 | ||||
| 	// stringsIdx maps previously encoded strings to their index within
 | ||||
| 	// the RelocString section, to allow deduplication. That is,
 | ||||
| 	// elems[RelocString][stringsIdx[s]] == s (if present).
 | ||||
| 	stringsIdx map[string]Index | ||||
| 
 | ||||
| 	// syncFrames is the number of frames to write at each sync
 | ||||
| 	// marker. A negative value means sync markers are omitted.
 | ||||
| 	syncFrames int | ||||
| } | ||||
| 
 | ||||
| // SyncMarkers reports whether pw uses sync markers.
 | ||||
| func (pw *PkgEncoder) SyncMarkers() bool { return pw.syncFrames >= 0 } | ||||
| 
 | ||||
| // NewPkgEncoder returns an initialized PkgEncoder.
 | ||||
| //
 | ||||
| // syncFrames is the number of caller frames that should be serialized
 | ||||
| // at Sync points. Serializing additional frames results in larger
 | ||||
| // export data files, but can help diagnosing desync errors in
 | ||||
| // higher-level Unified IR reader/writer code. If syncFrames is
 | ||||
| // negative, then sync markers are omitted entirely.
 | ||||
| func NewPkgEncoder(syncFrames int) PkgEncoder { | ||||
| 	return PkgEncoder{ | ||||
| 		stringsIdx: make(map[string]Index), | ||||
| 		syncFrames: syncFrames, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // DumpTo writes the package's encoded data to out0 and returns the
 | ||||
| // package fingerprint.
 | ||||
| func (pw *PkgEncoder) DumpTo(out0 io.Writer) (fingerprint [8]byte) { | ||||
| 	h := md5.New() | ||||
| 	out := io.MultiWriter(out0, h) | ||||
| 
 | ||||
| 	writeUint32 := func(x uint32) { | ||||
| 		assert(binary.Write(out, binary.LittleEndian, x) == nil) | ||||
| 	} | ||||
| 
 | ||||
| 	writeUint32(currentVersion) | ||||
| 
 | ||||
| 	var flags uint32 | ||||
| 	if pw.SyncMarkers() { | ||||
| 		flags |= flagSyncMarkers | ||||
| 	} | ||||
| 	writeUint32(flags) | ||||
| 
 | ||||
| 	// Write elemEndsEnds.
 | ||||
| 	var sum uint32 | ||||
| 	for _, elems := range &pw.elems { | ||||
| 		sum += uint32(len(elems)) | ||||
| 		writeUint32(sum) | ||||
| 	} | ||||
| 
 | ||||
| 	// Write elemEnds.
 | ||||
| 	sum = 0 | ||||
| 	for _, elems := range &pw.elems { | ||||
| 		for _, elem := range elems { | ||||
| 			sum += uint32(len(elem)) | ||||
| 			writeUint32(sum) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Write elemData.
 | ||||
| 	for _, elems := range &pw.elems { | ||||
| 		for _, elem := range elems { | ||||
| 			_, err := io.WriteString(out, elem) | ||||
| 			assert(err == nil) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Write fingerprint.
 | ||||
| 	copy(fingerprint[:], h.Sum(nil)) | ||||
| 	_, err := out0.Write(fingerprint[:]) | ||||
| 	assert(err == nil) | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // StringIdx adds a string value to the strings section, if not
 | ||||
| // already present, and returns its index.
 | ||||
| func (pw *PkgEncoder) StringIdx(s string) Index { | ||||
| 	if idx, ok := pw.stringsIdx[s]; ok { | ||||
| 		assert(pw.elems[RelocString][idx] == s) | ||||
| 		return idx | ||||
| 	} | ||||
| 
 | ||||
| 	idx := Index(len(pw.elems[RelocString])) | ||||
| 	pw.elems[RelocString] = append(pw.elems[RelocString], s) | ||||
| 	pw.stringsIdx[s] = idx | ||||
| 	return idx | ||||
| } | ||||
| 
 | ||||
| // NewEncoder returns an Encoder for a new element within the given
 | ||||
| // section, and encodes the given SyncMarker as the start of the
 | ||||
| // element bitstream.
 | ||||
| func (pw *PkgEncoder) NewEncoder(k RelocKind, marker SyncMarker) Encoder { | ||||
| 	e := pw.NewEncoderRaw(k) | ||||
| 	e.Sync(marker) | ||||
| 	return e | ||||
| } | ||||
| 
 | ||||
| // NewEncoderRaw returns an Encoder for a new element within the given
 | ||||
| // section.
 | ||||
| //
 | ||||
| // Most callers should use NewEncoder instead.
 | ||||
| func (pw *PkgEncoder) NewEncoderRaw(k RelocKind) Encoder { | ||||
| 	idx := Index(len(pw.elems[k])) | ||||
| 	pw.elems[k] = append(pw.elems[k], "") // placeholder
 | ||||
| 
 | ||||
| 	return Encoder{ | ||||
| 		p:   pw, | ||||
| 		k:   k, | ||||
| 		Idx: idx, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // An Encoder provides methods for encoding an individual element's
 | ||||
| // bitstream data.
 | ||||
| type Encoder struct { | ||||
| 	p *PkgEncoder | ||||
| 
 | ||||
| 	Relocs []RelocEnt | ||||
| 	Data   bytes.Buffer // accumulated element bitstream data
 | ||||
| 
 | ||||
| 	encodingRelocHeader bool | ||||
| 
 | ||||
| 	k   RelocKind | ||||
| 	Idx Index // index within relocation section
 | ||||
| } | ||||
| 
 | ||||
| // Flush finalizes the element's bitstream and returns its Index.
 | ||||
| func (w *Encoder) Flush() Index { | ||||
| 	var sb bytes.Buffer // TODO(mdempsky): strings.Builder after #44505 is resolved
 | ||||
| 
 | ||||
| 	// Backup the data so we write the relocations at the front.
 | ||||
| 	var tmp bytes.Buffer | ||||
| 	io.Copy(&tmp, &w.Data) | ||||
| 
 | ||||
| 	// TODO(mdempsky): Consider writing these out separately so they're
 | ||||
| 	// easier to strip, along with function bodies, so that we can prune
 | ||||
| 	// down to just the data that's relevant to go/types.
 | ||||
| 	if w.encodingRelocHeader { | ||||
| 		panic("encodingRelocHeader already true; recursive flush?") | ||||
| 	} | ||||
| 	w.encodingRelocHeader = true | ||||
| 	w.Sync(SyncRelocs) | ||||
| 	w.Len(len(w.Relocs)) | ||||
| 	for _, rEnt := range w.Relocs { | ||||
| 		w.Sync(SyncReloc) | ||||
| 		w.Len(int(rEnt.Kind)) | ||||
| 		w.Len(int(rEnt.Idx)) | ||||
| 	} | ||||
| 
 | ||||
| 	io.Copy(&sb, &w.Data) | ||||
| 	io.Copy(&sb, &tmp) | ||||
| 	w.p.elems[w.k][w.Idx] = sb.String() | ||||
| 
 | ||||
| 	return w.Idx | ||||
| } | ||||
| 
 | ||||
| func (w *Encoder) checkErr(err error) { | ||||
| 	if err != nil { | ||||
| 		errorf("unexpected encoding error: %v", err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (w *Encoder) rawUvarint(x uint64) { | ||||
| 	var buf [binary.MaxVarintLen64]byte | ||||
| 	n := binary.PutUvarint(buf[:], x) | ||||
| 	_, err := w.Data.Write(buf[:n]) | ||||
| 	w.checkErr(err) | ||||
| } | ||||
| 
 | ||||
| func (w *Encoder) rawVarint(x int64) { | ||||
| 	// Zig-zag encode.
 | ||||
| 	ux := uint64(x) << 1 | ||||
| 	if x < 0 { | ||||
| 		ux = ^ux | ||||
| 	} | ||||
| 
 | ||||
| 	w.rawUvarint(ux) | ||||
| } | ||||
| 
 | ||||
| func (w *Encoder) rawReloc(r RelocKind, idx Index) int { | ||||
| 	// TODO(mdempsky): Use map for lookup; this takes quadratic time.
 | ||||
| 	for i, rEnt := range w.Relocs { | ||||
| 		if rEnt.Kind == r && rEnt.Idx == idx { | ||||
| 			return i | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	i := len(w.Relocs) | ||||
| 	w.Relocs = append(w.Relocs, RelocEnt{r, idx}) | ||||
| 	return i | ||||
| } | ||||
| 
 | ||||
| func (w *Encoder) Sync(m SyncMarker) { | ||||
| 	if !w.p.SyncMarkers() { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// Writing out stack frame string references requires working
 | ||||
| 	// relocations, but writing out the relocations themselves involves
 | ||||
| 	// sync markers. To prevent infinite recursion, we simply trim the
 | ||||
| 	// stack frame for sync markers within the relocation header.
 | ||||
| 	var frames []string | ||||
| 	if !w.encodingRelocHeader && w.p.syncFrames > 0 { | ||||
| 		pcs := make([]uintptr, w.p.syncFrames) | ||||
| 		n := runtime.Callers(2, pcs) | ||||
| 		frames = fmtFrames(pcs[:n]...) | ||||
| 	} | ||||
| 
 | ||||
| 	// TODO(mdempsky): Save space by writing out stack frames as a
 | ||||
| 	// linked list so we can share common stack frames.
 | ||||
| 	w.rawUvarint(uint64(m)) | ||||
| 	w.rawUvarint(uint64(len(frames))) | ||||
| 	for _, frame := range frames { | ||||
| 		w.rawUvarint(uint64(w.rawReloc(RelocString, w.p.StringIdx(frame)))) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Bool encodes and writes a bool value into the element bitstream,
 | ||||
| // and then returns the bool value.
 | ||||
| //
 | ||||
| // For simple, 2-alternative encodings, the idiomatic way to call Bool
 | ||||
| // is something like:
 | ||||
| //
 | ||||
| //	if w.Bool(x != 0) {
 | ||||
| //		// alternative #1
 | ||||
| //	} else {
 | ||||
| //		// alternative #2
 | ||||
| //	}
 | ||||
| //
 | ||||
| // For multi-alternative encodings, use Code instead.
 | ||||
| func (w *Encoder) Bool(b bool) bool { | ||||
| 	w.Sync(SyncBool) | ||||
| 	var x byte | ||||
| 	if b { | ||||
| 		x = 1 | ||||
| 	} | ||||
| 	err := w.Data.WriteByte(x) | ||||
| 	w.checkErr(err) | ||||
| 	return b | ||||
| } | ||||
| 
 | ||||
| // Int64 encodes and writes an int64 value into the element bitstream.
 | ||||
| func (w *Encoder) Int64(x int64) { | ||||
| 	w.Sync(SyncInt64) | ||||
| 	w.rawVarint(x) | ||||
| } | ||||
| 
 | ||||
| // Uint64 encodes and writes a uint64 value into the element bitstream.
 | ||||
| func (w *Encoder) Uint64(x uint64) { | ||||
| 	w.Sync(SyncUint64) | ||||
| 	w.rawUvarint(x) | ||||
| } | ||||
| 
 | ||||
| // Len encodes and writes a non-negative int value into the element bitstream.
 | ||||
| func (w *Encoder) Len(x int) { assert(x >= 0); w.Uint64(uint64(x)) } | ||||
| 
 | ||||
| // Int encodes and writes an int value into the element bitstream.
 | ||||
| func (w *Encoder) Int(x int) { w.Int64(int64(x)) } | ||||
| 
 | ||||
| // Len encodes and writes a uint value into the element bitstream.
 | ||||
| func (w *Encoder) Uint(x uint) { w.Uint64(uint64(x)) } | ||||
| 
 | ||||
| // Reloc encodes and writes a relocation for the given (section,
 | ||||
| // index) pair into the element bitstream.
 | ||||
| //
 | ||||
| // Note: Only the index is formally written into the element
 | ||||
| // bitstream, so bitstream decoders must know from context which
 | ||||
| // section an encoded relocation refers to.
 | ||||
| func (w *Encoder) Reloc(r RelocKind, idx Index) { | ||||
| 	w.Sync(SyncUseReloc) | ||||
| 	w.Len(w.rawReloc(r, idx)) | ||||
| } | ||||
| 
 | ||||
| // Code encodes and writes a Code value into the element bitstream.
 | ||||
| func (w *Encoder) Code(c Code) { | ||||
| 	w.Sync(c.Marker()) | ||||
| 	w.Len(c.Value()) | ||||
| } | ||||
| 
 | ||||
| // String encodes and writes a string value into the element
 | ||||
| // bitstream.
 | ||||
| //
 | ||||
| // Internally, strings are deduplicated by adding them to the strings
 | ||||
| // section (if not already present), and then writing a relocation
 | ||||
| // into the element bitstream.
 | ||||
| func (w *Encoder) String(s string) { | ||||
| 	w.Sync(SyncString) | ||||
| 	w.Reloc(RelocString, w.p.StringIdx(s)) | ||||
| } | ||||
| 
 | ||||
| // Strings encodes and writes a variable-length slice of strings into
 | ||||
| // the element bitstream.
 | ||||
| func (w *Encoder) Strings(ss []string) { | ||||
| 	w.Len(len(ss)) | ||||
| 	for _, s := range ss { | ||||
| 		w.String(s) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Value encodes and writes a constant.Value into the element
 | ||||
| // bitstream.
 | ||||
| func (w *Encoder) Value(val constant.Value) { | ||||
| 	w.Sync(SyncValue) | ||||
| 	if w.Bool(val.Kind() == constant.Complex) { | ||||
| 		w.scalar(constant.Real(val)) | ||||
| 		w.scalar(constant.Imag(val)) | ||||
| 	} else { | ||||
| 		w.scalar(val) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (w *Encoder) scalar(val constant.Value) { | ||||
| 	switch v := constant.Val(val).(type) { | ||||
| 	default: | ||||
| 		errorf("unhandled %v (%v)", val, val.Kind()) | ||||
| 	case bool: | ||||
| 		w.Code(ValBool) | ||||
| 		w.Bool(v) | ||||
| 	case string: | ||||
| 		w.Code(ValString) | ||||
| 		w.String(v) | ||||
| 	case int64: | ||||
| 		w.Code(ValInt64) | ||||
| 		w.Int64(v) | ||||
| 	case *big.Int: | ||||
| 		w.Code(ValBigInt) | ||||
| 		w.bigInt(v) | ||||
| 	case *big.Rat: | ||||
| 		w.Code(ValBigRat) | ||||
| 		w.bigInt(v.Num()) | ||||
| 		w.bigInt(v.Denom()) | ||||
| 	case *big.Float: | ||||
| 		w.Code(ValBigFloat) | ||||
| 		w.bigFloat(v) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (w *Encoder) bigInt(v *big.Int) { | ||||
| 	b := v.Bytes() | ||||
| 	w.String(string(b)) // TODO: More efficient encoding.
 | ||||
| 	w.Bool(v.Sign() < 0) | ||||
| } | ||||
| 
 | ||||
| func (w *Encoder) bigFloat(v *big.Float) { | ||||
| 	b := v.Append(nil, 'p', -1) | ||||
| 	w.String(string(b)) // TODO: More efficient encoding.
 | ||||
| } | ||||
|  | @ -0,0 +1,9 @@ | |||
| // Copyright 2022 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.
 | ||||
| 
 | ||||
| package pkgbits | ||||
| 
 | ||||
| const ( | ||||
| 	flagSyncMarkers = 1 << iota // file format contains sync markers
 | ||||
| ) | ||||
|  | @ -0,0 +1,21 @@ | |||
| // Copyright 2021 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.
 | ||||
| 
 | ||||
| //go:build !go1.7
 | ||||
| // +build !go1.7
 | ||||
| 
 | ||||
| // TODO(mdempsky): Remove after #44505 is resolved
 | ||||
| 
 | ||||
| package pkgbits | ||||
| 
 | ||||
| import "runtime" | ||||
| 
 | ||||
| func walkFrames(pcs []uintptr, visit frameVisitor) { | ||||
| 	for _, pc := range pcs { | ||||
| 		fn := runtime.FuncForPC(pc) | ||||
| 		file, line := fn.FileLine(pc) | ||||
| 
 | ||||
| 		visit(file, line, fn.Name(), pc-fn.Entry()) | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,28 @@ | |||
| // Copyright 2021 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.
 | ||||
| 
 | ||||
| //go:build go1.7
 | ||||
| // +build go1.7
 | ||||
| 
 | ||||
| package pkgbits | ||||
| 
 | ||||
| import "runtime" | ||||
| 
 | ||||
| // walkFrames calls visit for each call frame represented by pcs.
 | ||||
| //
 | ||||
| // pcs should be a slice of PCs, as returned by runtime.Callers.
 | ||||
| func walkFrames(pcs []uintptr, visit frameVisitor) { | ||||
| 	if len(pcs) == 0 { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	frames := runtime.CallersFrames(pcs) | ||||
| 	for { | ||||
| 		frame, more := frames.Next() | ||||
| 		visit(frame.File, frame.Line, frame.Function, frame.PC-frame.Entry) | ||||
| 		if !more { | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,42 @@ | |||
| // Copyright 2021 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.
 | ||||
| 
 | ||||
| package pkgbits | ||||
| 
 | ||||
| // A RelocKind indicates a particular section within a unified IR export.
 | ||||
| type RelocKind int | ||||
| 
 | ||||
| // An Index represents a bitstream element index within a particular
 | ||||
| // section.
 | ||||
| type Index int | ||||
| 
 | ||||
| // A relocEnt (relocation entry) is an entry in an element's local
 | ||||
| // reference table.
 | ||||
| //
 | ||||
| // TODO(mdempsky): Rename this too.
 | ||||
| type RelocEnt struct { | ||||
| 	Kind RelocKind | ||||
| 	Idx  Index | ||||
| } | ||||
| 
 | ||||
| // Reserved indices within the meta relocation section.
 | ||||
| const ( | ||||
| 	PublicRootIdx  Index = 0 | ||||
| 	PrivateRootIdx Index = 1 | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	RelocString RelocKind = iota | ||||
| 	RelocMeta | ||||
| 	RelocPosBase | ||||
| 	RelocPkg | ||||
| 	RelocName | ||||
| 	RelocType | ||||
| 	RelocObj | ||||
| 	RelocObjExt | ||||
| 	RelocObjDict | ||||
| 	RelocBody | ||||
| 
 | ||||
| 	numRelocs = iota | ||||
| ) | ||||
|  | @ -0,0 +1,17 @@ | |||
| // Copyright 2022 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.
 | ||||
| 
 | ||||
| package pkgbits | ||||
| 
 | ||||
| import "fmt" | ||||
| 
 | ||||
| func assert(b bool) { | ||||
| 	if !b { | ||||
| 		panic("assertion failed") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func errorf(format string, args ...interface{}) { | ||||
| 	panic(fmt.Errorf(format, args...)) | ||||
| } | ||||
|  | @ -0,0 +1,113 @@ | |||
| // Copyright 2021 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.
 | ||||
| 
 | ||||
| package pkgbits | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // fmtFrames formats a backtrace for reporting reader/writer desyncs.
 | ||||
| func fmtFrames(pcs ...uintptr) []string { | ||||
| 	res := make([]string, 0, len(pcs)) | ||||
| 	walkFrames(pcs, func(file string, line int, name string, offset uintptr) { | ||||
| 		// Trim package from function name. It's just redundant noise.
 | ||||
| 		name = strings.TrimPrefix(name, "cmd/compile/internal/noder.") | ||||
| 
 | ||||
| 		res = append(res, fmt.Sprintf("%s:%v: %s +0x%v", file, line, name, offset)) | ||||
| 	}) | ||||
| 	return res | ||||
| } | ||||
| 
 | ||||
| type frameVisitor func(file string, line int, name string, offset uintptr) | ||||
| 
 | ||||
| // SyncMarker is an enum type that represents markers that may be
 | ||||
| // written to export data to ensure the reader and writer stay
 | ||||
| // synchronized.
 | ||||
| type SyncMarker int | ||||
| 
 | ||||
| //go:generate stringer -type=SyncMarker -trimprefix=Sync
 | ||||
| 
 | ||||
| const ( | ||||
| 	_ SyncMarker = iota | ||||
| 
 | ||||
| 	// Public markers (known to go/types importers).
 | ||||
| 
 | ||||
| 	// Low-level coding markers.
 | ||||
| 	SyncEOF | ||||
| 	SyncBool | ||||
| 	SyncInt64 | ||||
| 	SyncUint64 | ||||
| 	SyncString | ||||
| 	SyncValue | ||||
| 	SyncVal | ||||
| 	SyncRelocs | ||||
| 	SyncReloc | ||||
| 	SyncUseReloc | ||||
| 
 | ||||
| 	// Higher-level object and type markers.
 | ||||
| 	SyncPublic | ||||
| 	SyncPos | ||||
| 	SyncPosBase | ||||
| 	SyncObject | ||||
| 	SyncObject1 | ||||
| 	SyncPkg | ||||
| 	SyncPkgDef | ||||
| 	SyncMethod | ||||
| 	SyncType | ||||
| 	SyncTypeIdx | ||||
| 	SyncTypeParamNames | ||||
| 	SyncSignature | ||||
| 	SyncParams | ||||
| 	SyncParam | ||||
| 	SyncCodeObj | ||||
| 	SyncSym | ||||
| 	SyncLocalIdent | ||||
| 	SyncSelector | ||||
| 
 | ||||
| 	// Private markers (only known to cmd/compile).
 | ||||
| 	SyncPrivate | ||||
| 
 | ||||
| 	SyncFuncExt | ||||
| 	SyncVarExt | ||||
| 	SyncTypeExt | ||||
| 	SyncPragma | ||||
| 
 | ||||
| 	SyncExprList | ||||
| 	SyncExprs | ||||
| 	SyncExpr | ||||
| 	SyncExprType | ||||
| 	SyncAssign | ||||
| 	SyncOp | ||||
| 	SyncFuncLit | ||||
| 	SyncCompLit | ||||
| 
 | ||||
| 	SyncDecl | ||||
| 	SyncFuncBody | ||||
| 	SyncOpenScope | ||||
| 	SyncCloseScope | ||||
| 	SyncCloseAnotherScope | ||||
| 	SyncDeclNames | ||||
| 	SyncDeclName | ||||
| 
 | ||||
| 	SyncStmts | ||||
| 	SyncBlockStmt | ||||
| 	SyncIfStmt | ||||
| 	SyncForStmt | ||||
| 	SyncSwitchStmt | ||||
| 	SyncRangeStmt | ||||
| 	SyncCaseClause | ||||
| 	SyncCommClause | ||||
| 	SyncSelectStmt | ||||
| 	SyncDecls | ||||
| 	SyncLabeledStmt | ||||
| 	SyncUseObjLocal | ||||
| 	SyncAddLocal | ||||
| 	SyncLinkname | ||||
| 	SyncStmt1 | ||||
| 	SyncStmtsEnd | ||||
| 	SyncLabel | ||||
| 	SyncOptLabel | ||||
| ) | ||||
							
								
								
									
										89
									
								
								vendor/golang.org/x/tools/go/internal/pkgbits/syncmarker_string.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										89
									
								
								vendor/golang.org/x/tools/go/internal/pkgbits/syncmarker_string.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							|  | @ -0,0 +1,89 @@ | |||
| // Code generated by "stringer -type=SyncMarker -trimprefix=Sync"; DO NOT EDIT.
 | ||||
| 
 | ||||
| package pkgbits | ||||
| 
 | ||||
| import "strconv" | ||||
| 
 | ||||
| func _() { | ||||
| 	// An "invalid array index" compiler error signifies that the constant values have changed.
 | ||||
| 	// Re-run the stringer command to generate them again.
 | ||||
| 	var x [1]struct{} | ||||
| 	_ = x[SyncEOF-1] | ||||
| 	_ = x[SyncBool-2] | ||||
| 	_ = x[SyncInt64-3] | ||||
| 	_ = x[SyncUint64-4] | ||||
| 	_ = x[SyncString-5] | ||||
| 	_ = x[SyncValue-6] | ||||
| 	_ = x[SyncVal-7] | ||||
| 	_ = x[SyncRelocs-8] | ||||
| 	_ = x[SyncReloc-9] | ||||
| 	_ = x[SyncUseReloc-10] | ||||
| 	_ = x[SyncPublic-11] | ||||
| 	_ = x[SyncPos-12] | ||||
| 	_ = x[SyncPosBase-13] | ||||
| 	_ = x[SyncObject-14] | ||||
| 	_ = x[SyncObject1-15] | ||||
| 	_ = x[SyncPkg-16] | ||||
| 	_ = x[SyncPkgDef-17] | ||||
| 	_ = x[SyncMethod-18] | ||||
| 	_ = x[SyncType-19] | ||||
| 	_ = x[SyncTypeIdx-20] | ||||
| 	_ = x[SyncTypeParamNames-21] | ||||
| 	_ = x[SyncSignature-22] | ||||
| 	_ = x[SyncParams-23] | ||||
| 	_ = x[SyncParam-24] | ||||
| 	_ = x[SyncCodeObj-25] | ||||
| 	_ = x[SyncSym-26] | ||||
| 	_ = x[SyncLocalIdent-27] | ||||
| 	_ = x[SyncSelector-28] | ||||
| 	_ = x[SyncPrivate-29] | ||||
| 	_ = x[SyncFuncExt-30] | ||||
| 	_ = x[SyncVarExt-31] | ||||
| 	_ = x[SyncTypeExt-32] | ||||
| 	_ = x[SyncPragma-33] | ||||
| 	_ = x[SyncExprList-34] | ||||
| 	_ = x[SyncExprs-35] | ||||
| 	_ = x[SyncExpr-36] | ||||
| 	_ = x[SyncExprType-37] | ||||
| 	_ = x[SyncAssign-38] | ||||
| 	_ = x[SyncOp-39] | ||||
| 	_ = x[SyncFuncLit-40] | ||||
| 	_ = x[SyncCompLit-41] | ||||
| 	_ = x[SyncDecl-42] | ||||
| 	_ = x[SyncFuncBody-43] | ||||
| 	_ = x[SyncOpenScope-44] | ||||
| 	_ = x[SyncCloseScope-45] | ||||
| 	_ = x[SyncCloseAnotherScope-46] | ||||
| 	_ = x[SyncDeclNames-47] | ||||
| 	_ = x[SyncDeclName-48] | ||||
| 	_ = x[SyncStmts-49] | ||||
| 	_ = x[SyncBlockStmt-50] | ||||
| 	_ = x[SyncIfStmt-51] | ||||
| 	_ = x[SyncForStmt-52] | ||||
| 	_ = x[SyncSwitchStmt-53] | ||||
| 	_ = x[SyncRangeStmt-54] | ||||
| 	_ = x[SyncCaseClause-55] | ||||
| 	_ = x[SyncCommClause-56] | ||||
| 	_ = x[SyncSelectStmt-57] | ||||
| 	_ = x[SyncDecls-58] | ||||
| 	_ = x[SyncLabeledStmt-59] | ||||
| 	_ = x[SyncUseObjLocal-60] | ||||
| 	_ = x[SyncAddLocal-61] | ||||
| 	_ = x[SyncLinkname-62] | ||||
| 	_ = x[SyncStmt1-63] | ||||
| 	_ = x[SyncStmtsEnd-64] | ||||
| 	_ = x[SyncLabel-65] | ||||
| 	_ = x[SyncOptLabel-66] | ||||
| } | ||||
| 
 | ||||
| const _SyncMarker_name = "EOFBoolInt64Uint64StringValueValRelocsRelocUseRelocPublicPosPosBaseObjectObject1PkgPkgDefMethodTypeTypeIdxTypeParamNamesSignatureParamsParamCodeObjSymLocalIdentSelectorPrivateFuncExtVarExtTypeExtPragmaExprListExprsExprExprTypeAssignOpFuncLitCompLitDeclFuncBodyOpenScopeCloseScopeCloseAnotherScopeDeclNamesDeclNameStmtsBlockStmtIfStmtForStmtSwitchStmtRangeStmtCaseClauseCommClauseSelectStmtDeclsLabeledStmtUseObjLocalAddLocalLinknameStmt1StmtsEndLabelOptLabel" | ||||
| 
 | ||||
| var _SyncMarker_index = [...]uint16{0, 3, 7, 12, 18, 24, 29, 32, 38, 43, 51, 57, 60, 67, 73, 80, 83, 89, 95, 99, 106, 120, 129, 135, 140, 147, 150, 160, 168, 175, 182, 188, 195, 201, 209, 214, 218, 226, 232, 234, 241, 248, 252, 260, 269, 279, 296, 305, 313, 318, 327, 333, 340, 350, 359, 369, 379, 389, 394, 405, 416, 424, 432, 437, 445, 450, 458} | ||||
| 
 | ||||
| func (i SyncMarker) String() string { | ||||
| 	i -= 1 | ||||
| 	if i < 0 || i >= SyncMarker(len(_SyncMarker_index)-1) { | ||||
| 		return "SyncMarker(" + strconv.FormatInt(int64(i+1), 10) + ")" | ||||
| 	} | ||||
| 	return _SyncMarker_name[_SyncMarker_index[i]:_SyncMarker_index[i+1]] | ||||
| } | ||||
|  | @ -0,0 +1,220 @@ | |||
| // Copyright 2018 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.
 | ||||
| 
 | ||||
| /* | ||||
| Package packages loads Go packages for inspection and analysis. | ||||
| 
 | ||||
| The Load function takes as input a list of patterns and return a list of Package | ||||
| structs describing individual packages matched by those patterns. | ||||
| The LoadMode controls the amount of detail in the loaded packages. | ||||
| 
 | ||||
| Load passes most patterns directly to the underlying build tool, | ||||
| but all patterns with the prefix "query=", where query is a | ||||
| non-empty string of letters from [a-z], are reserved and may be | ||||
| interpreted as query operators. | ||||
| 
 | ||||
| Two query operators are currently supported: "file" and "pattern". | ||||
| 
 | ||||
| The query "file=path/to/file.go" matches the package or packages enclosing | ||||
| the Go source file path/to/file.go.  For example "file=~/go/src/fmt/print.go" | ||||
| might return the packages "fmt" and "fmt [fmt.test]". | ||||
| 
 | ||||
| The query "pattern=string" causes "string" to be passed directly to | ||||
| the underlying build tool. In most cases this is unnecessary, | ||||
| but an application can use Load("pattern=" + x) as an escaping mechanism | ||||
| to ensure that x is not interpreted as a query operator if it contains '='. | ||||
| 
 | ||||
| All other query operators are reserved for future use and currently | ||||
| cause Load to report an error. | ||||
| 
 | ||||
| The Package struct provides basic information about the package, including | ||||
| 
 | ||||
|   - ID, a unique identifier for the package in the returned set; | ||||
|   - GoFiles, the names of the package's Go source files; | ||||
|   - Imports, a map from source import strings to the Packages they name; | ||||
|   - Types, the type information for the package's exported symbols; | ||||
|   - Syntax, the parsed syntax trees for the package's source code; and | ||||
|   - TypeInfo, the result of a complete type-check of the package syntax trees. | ||||
| 
 | ||||
| (See the documentation for type Package for the complete list of fields | ||||
| and more detailed descriptions.) | ||||
| 
 | ||||
| For example, | ||||
| 
 | ||||
| 	Load(nil, "bytes", "unicode...") | ||||
| 
 | ||||
| returns four Package structs describing the standard library packages | ||||
| bytes, unicode, unicode/utf16, and unicode/utf8. Note that one pattern | ||||
| can match multiple packages and that a package might be matched by | ||||
| multiple patterns: in general it is not possible to determine which | ||||
| packages correspond to which patterns. | ||||
| 
 | ||||
| Note that the list returned by Load contains only the packages matched | ||||
| by the patterns. Their dependencies can be found by walking the import | ||||
| graph using the Imports fields. | ||||
| 
 | ||||
| The Load function can be configured by passing a pointer to a Config as | ||||
| the first argument. A nil Config is equivalent to the zero Config, which | ||||
| causes Load to run in LoadFiles mode, collecting minimal information. | ||||
| See the documentation for type Config for details. | ||||
| 
 | ||||
| As noted earlier, the Config.Mode controls the amount of detail | ||||
| reported about the loaded packages. See the documentation for type LoadMode | ||||
| for details. | ||||
| 
 | ||||
| Most tools should pass their command-line arguments (after any flags) | ||||
| uninterpreted to the loader, so that the loader can interpret them | ||||
| according to the conventions of the underlying build system. | ||||
| See the Example function for typical usage. | ||||
| */ | ||||
| package packages // import "golang.org/x/tools/go/packages"
 | ||||
| 
 | ||||
| /* | ||||
| 
 | ||||
| Motivation and design considerations | ||||
| 
 | ||||
| The new package's design solves problems addressed by two existing | ||||
| packages: go/build, which locates and describes packages, and | ||||
| golang.org/x/tools/go/loader, which loads, parses and type-checks them. | ||||
| The go/build.Package structure encodes too much of the 'go build' way | ||||
| of organizing projects, leaving us in need of a data type that describes a | ||||
| package of Go source code independent of the underlying build system. | ||||
| We wanted something that works equally well with go build and vgo, and | ||||
| also other build systems such as Bazel and Blaze, making it possible to | ||||
| construct analysis tools that work in all these environments. | ||||
| Tools such as errcheck and staticcheck were essentially unavailable to | ||||
| the Go community at Google, and some of Google's internal tools for Go | ||||
| are unavailable externally. | ||||
| This new package provides a uniform way to obtain package metadata by | ||||
| querying each of these build systems, optionally supporting their | ||||
| preferred command-line notations for packages, so that tools integrate | ||||
| neatly with users' build environments. The Metadata query function | ||||
| executes an external query tool appropriate to the current workspace. | ||||
| 
 | ||||
| Loading packages always returns the complete import graph "all the way down", | ||||
| even if all you want is information about a single package, because the query | ||||
| mechanisms of all the build systems we currently support ({go,vgo} list, and | ||||
| blaze/bazel aspect-based query) cannot provide detailed information | ||||
| about one package without visiting all its dependencies too, so there is | ||||
| no additional asymptotic cost to providing transitive information. | ||||
| (This property might not be true of a hypothetical 5th build system.) | ||||
| 
 | ||||
| In calls to TypeCheck, all initial packages, and any package that | ||||
| transitively depends on one of them, must be loaded from source. | ||||
| Consider A->B->C->D->E: if A,C are initial, A,B,C must be loaded from | ||||
| source; D may be loaded from export data, and E may not be loaded at all | ||||
| (though it's possible that D's export data mentions it, so a | ||||
| types.Package may be created for it and exposed.) | ||||
| 
 | ||||
| The old loader had a feature to suppress type-checking of function | ||||
| bodies on a per-package basis, primarily intended to reduce the work of | ||||
| obtaining type information for imported packages. Now that imports are | ||||
| satisfied by export data, the optimization no longer seems necessary. | ||||
| 
 | ||||
| Despite some early attempts, the old loader did not exploit export data, | ||||
| instead always using the equivalent of WholeProgram mode. This was due | ||||
| to the complexity of mixing source and export data packages (now | ||||
| resolved by the upward traversal mentioned above), and because export data | ||||
| files were nearly always missing or stale. Now that 'go build' supports | ||||
| caching, all the underlying build systems can guarantee to produce | ||||
| export data in a reasonable (amortized) time. | ||||
| 
 | ||||
| Test "main" packages synthesized by the build system are now reported as | ||||
| first-class packages, avoiding the need for clients (such as go/ssa) to | ||||
| reinvent this generation logic. | ||||
| 
 | ||||
| One way in which go/packages is simpler than the old loader is in its | ||||
| treatment of in-package tests. In-package tests are packages that | ||||
| consist of all the files of the library under test, plus the test files. | ||||
| The old loader constructed in-package tests by a two-phase process of | ||||
| mutation called "augmentation": first it would construct and type check | ||||
| all the ordinary library packages and type-check the packages that | ||||
| depend on them; then it would add more (test) files to the package and | ||||
| type-check again. This two-phase approach had four major problems: | ||||
| 1) in processing the tests, the loader modified the library package, | ||||
|    leaving no way for a client application to see both the test | ||||
|    package and the library package; one would mutate into the other. | ||||
| 2) because test files can declare additional methods on types defined in | ||||
|    the library portion of the package, the dispatch of method calls in | ||||
|    the library portion was affected by the presence of the test files. | ||||
|    This should have been a clue that the packages were logically | ||||
|    different. | ||||
| 3) this model of "augmentation" assumed at most one in-package test | ||||
|    per library package, which is true of projects using 'go build', | ||||
|    but not other build systems. | ||||
| 4) because of the two-phase nature of test processing, all packages that | ||||
|    import the library package had to be processed before augmentation, | ||||
|    forcing a "one-shot" API and preventing the client from calling Load | ||||
|    in several times in sequence as is now possible in WholeProgram mode. | ||||
|    (TypeCheck mode has a similar one-shot restriction for a different reason.) | ||||
| 
 | ||||
| Early drafts of this package supported "multi-shot" operation. | ||||
| Although it allowed clients to make a sequence of calls (or concurrent | ||||
| calls) to Load, building up the graph of Packages incrementally, | ||||
| it was of marginal value: it complicated the API | ||||
| (since it allowed some options to vary across calls but not others), | ||||
| it complicated the implementation, | ||||
| it cannot be made to work in Types mode, as explained above, | ||||
| and it was less efficient than making one combined call (when this is possible). | ||||
| Among the clients we have inspected, none made multiple calls to load | ||||
| but could not be easily and satisfactorily modified to make only a single call. | ||||
| However, applications changes may be required. | ||||
| For example, the ssadump command loads the user-specified packages | ||||
| and in addition the runtime package.  It is tempting to simply append | ||||
| "runtime" to the user-provided list, but that does not work if the user | ||||
| specified an ad-hoc package such as [a.go b.go]. | ||||
| Instead, ssadump no longer requests the runtime package, | ||||
| but seeks it among the dependencies of the user-specified packages, | ||||
| and emits an error if it is not found. | ||||
| 
 | ||||
| Overlays: The Overlay field in the Config allows providing alternate contents | ||||
| for Go source files, by providing a mapping from file path to contents. | ||||
| go/packages will pull in new imports added in overlay files when go/packages | ||||
| is run in LoadImports mode or greater. | ||||
| Overlay support for the go list driver isn't complete yet: if the file doesn't | ||||
| exist on disk, it will only be recognized in an overlay if it is a non-test file | ||||
| and the package would be reported even without the overlay. | ||||
| 
 | ||||
| Questions & Tasks | ||||
| 
 | ||||
| - Add GOARCH/GOOS? | ||||
|   They are not portable concepts, but could be made portable. | ||||
|   Our goal has been to allow users to express themselves using the conventions | ||||
|   of the underlying build system: if the build system honors GOARCH | ||||
|   during a build and during a metadata query, then so should | ||||
|   applications built atop that query mechanism. | ||||
|   Conversely, if the target architecture of the build is determined by | ||||
|   command-line flags, the application can pass the relevant | ||||
|   flags through to the build system using a command such as: | ||||
|     myapp -query_flag="--cpu=amd64" -query_flag="--os=darwin" | ||||
|   However, this approach is low-level, unwieldy, and non-portable. | ||||
|   GOOS and GOARCH seem important enough to warrant a dedicated option. | ||||
| 
 | ||||
| - How should we handle partial failures such as a mixture of good and | ||||
|   malformed patterns, existing and non-existent packages, successful and | ||||
|   failed builds, import failures, import cycles, and so on, in a call to | ||||
|   Load? | ||||
| 
 | ||||
| - Support bazel, blaze, and go1.10 list, not just go1.11 list. | ||||
| 
 | ||||
| - Handle (and test) various partial success cases, e.g. | ||||
|   a mixture of good packages and: | ||||
|   invalid patterns | ||||
|   nonexistent packages | ||||
|   empty packages | ||||
|   packages with malformed package or import declarations | ||||
|   unreadable files | ||||
|   import cycles | ||||
|   other parse errors | ||||
|   type errors | ||||
|   Make sure we record errors at the correct place in the graph. | ||||
| 
 | ||||
| - Missing packages among initial arguments are not reported. | ||||
|   Return bogus packages for them, like golist does. | ||||
| 
 | ||||
| - "undeclared name" errors (for example) are reported out of source file | ||||
|   order. I suspect this is due to the breadth-first resolution now used | ||||
|   by go/types. Is that a bug? Discuss with gri. | ||||
| 
 | ||||
| */ | ||||
|  | @ -0,0 +1,101 @@ | |||
| // Copyright 2018 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.
 | ||||
| 
 | ||||
| // This file enables an external tool to intercept package requests.
 | ||||
| // If the tool is present then its results are used in preference to
 | ||||
| // the go list command.
 | ||||
| 
 | ||||
| package packages | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	exec "golang.org/x/sys/execabs" | ||||
| 	"os" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // The Driver Protocol
 | ||||
| //
 | ||||
| // The driver, given the inputs to a call to Load, returns metadata about the packages specified.
 | ||||
| // This allows for different build systems to support go/packages by telling go/packages how the
 | ||||
| // packages' source is organized.
 | ||||
| // The driver is a binary, either specified by the GOPACKAGESDRIVER environment variable or in
 | ||||
| // the path as gopackagesdriver. It's given the inputs to load in its argv. See the package
 | ||||
| // documentation in doc.go for the full description of the patterns that need to be supported.
 | ||||
| // A driver receives as a JSON-serialized driverRequest struct in standard input and will
 | ||||
| // produce a JSON-serialized driverResponse (see definition in packages.go) in its standard output.
 | ||||
| 
 | ||||
| // driverRequest is used to provide the portion of Load's Config that is needed by a driver.
 | ||||
| type driverRequest struct { | ||||
| 	Mode LoadMode `json:"mode"` | ||||
| 	// Env specifies the environment the underlying build system should be run in.
 | ||||
| 	Env []string `json:"env"` | ||||
| 	// BuildFlags are flags that should be passed to the underlying build system.
 | ||||
| 	BuildFlags []string `json:"build_flags"` | ||||
| 	// Tests specifies whether the patterns should also return test packages.
 | ||||
| 	Tests bool `json:"tests"` | ||||
| 	// Overlay maps file paths (relative to the driver's working directory) to the byte contents
 | ||||
| 	// of overlay files.
 | ||||
| 	Overlay map[string][]byte `json:"overlay"` | ||||
| } | ||||
| 
 | ||||
| // findExternalDriver returns the file path of a tool that supplies
 | ||||
| // the build system package structure, or "" if not found."
 | ||||
| // If GOPACKAGESDRIVER is set in the environment findExternalTool returns its
 | ||||
| // value, otherwise it searches for a binary named gopackagesdriver on the PATH.
 | ||||
| func findExternalDriver(cfg *Config) driver { | ||||
| 	const toolPrefix = "GOPACKAGESDRIVER=" | ||||
| 	tool := "" | ||||
| 	for _, env := range cfg.Env { | ||||
| 		if val := strings.TrimPrefix(env, toolPrefix); val != env { | ||||
| 			tool = val | ||||
| 		} | ||||
| 	} | ||||
| 	if tool != "" && tool == "off" { | ||||
| 		return nil | ||||
| 	} | ||||
| 	if tool == "" { | ||||
| 		var err error | ||||
| 		tool, err = exec.LookPath("gopackagesdriver") | ||||
| 		if err != nil { | ||||
| 			return nil | ||||
| 		} | ||||
| 	} | ||||
| 	return func(cfg *Config, words ...string) (*driverResponse, error) { | ||||
| 		req, err := json.Marshal(driverRequest{ | ||||
| 			Mode:       cfg.Mode, | ||||
| 			Env:        cfg.Env, | ||||
| 			BuildFlags: cfg.BuildFlags, | ||||
| 			Tests:      cfg.Tests, | ||||
| 			Overlay:    cfg.Overlay, | ||||
| 		}) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("failed to encode message to driver tool: %v", err) | ||||
| 		} | ||||
| 
 | ||||
| 		buf := new(bytes.Buffer) | ||||
| 		stderr := new(bytes.Buffer) | ||||
| 		cmd := exec.CommandContext(cfg.Context, tool, words...) | ||||
| 		cmd.Dir = cfg.Dir | ||||
| 		cmd.Env = cfg.Env | ||||
| 		cmd.Stdin = bytes.NewReader(req) | ||||
| 		cmd.Stdout = buf | ||||
| 		cmd.Stderr = stderr | ||||
| 
 | ||||
| 		if err := cmd.Run(); err != nil { | ||||
| 			return nil, fmt.Errorf("%v: %v: %s", tool, err, cmd.Stderr) | ||||
| 		} | ||||
| 		if len(stderr.Bytes()) != 0 && os.Getenv("GOPACKAGESPRINTDRIVERERRORS") != "" { | ||||
| 			fmt.Fprintf(os.Stderr, "%s stderr: <<%s>>\n", cmdDebugStr(cmd), stderr) | ||||
| 		} | ||||
| 
 | ||||
| 		var response driverResponse | ||||
| 		if err := json.Unmarshal(buf.Bytes(), &response); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		return &response, nil | ||||
| 	} | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,575 @@ | |||
| // Copyright 2018 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.
 | ||||
| 
 | ||||
| package packages | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"go/parser" | ||||
| 	"go/token" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"regexp" | ||||
| 	"sort" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"golang.org/x/tools/internal/gocommand" | ||||
| ) | ||||
| 
 | ||||
| // processGolistOverlay provides rudimentary support for adding
 | ||||
| // files that don't exist on disk to an overlay. The results can be
 | ||||
| // sometimes incorrect.
 | ||||
| // TODO(matloob): Handle unsupported cases, including the following:
 | ||||
| // - determining the correct package to add given a new import path
 | ||||
| func (state *golistState) processGolistOverlay(response *responseDeduper) (modifiedPkgs, needPkgs []string, err error) { | ||||
| 	havePkgs := make(map[string]string) // importPath -> non-test package ID
 | ||||
| 	needPkgsSet := make(map[string]bool) | ||||
| 	modifiedPkgsSet := make(map[string]bool) | ||||
| 
 | ||||
| 	pkgOfDir := make(map[string][]*Package) | ||||
| 	for _, pkg := range response.dr.Packages { | ||||
| 		// This is an approximation of import path to id. This can be
 | ||||
| 		// wrong for tests, vendored packages, and a number of other cases.
 | ||||
| 		havePkgs[pkg.PkgPath] = pkg.ID | ||||
| 		dir, err := commonDir(pkg.GoFiles) | ||||
| 		if err != nil { | ||||
| 			return nil, nil, err | ||||
| 		} | ||||
| 		if dir != "" { | ||||
| 			pkgOfDir[dir] = append(pkgOfDir[dir], pkg) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// If no new imports are added, it is safe to avoid loading any needPkgs.
 | ||||
| 	// Otherwise, it's hard to tell which package is actually being loaded
 | ||||
| 	// (due to vendoring) and whether any modified package will show up
 | ||||
| 	// in the transitive set of dependencies (because new imports are added,
 | ||||
| 	// potentially modifying the transitive set of dependencies).
 | ||||
| 	var overlayAddsImports bool | ||||
| 
 | ||||
| 	// If both a package and its test package are created by the overlay, we
 | ||||
| 	// need the real package first. Process all non-test files before test
 | ||||
| 	// files, and make the whole process deterministic while we're at it.
 | ||||
| 	var overlayFiles []string | ||||
| 	for opath := range state.cfg.Overlay { | ||||
| 		overlayFiles = append(overlayFiles, opath) | ||||
| 	} | ||||
| 	sort.Slice(overlayFiles, func(i, j int) bool { | ||||
| 		iTest := strings.HasSuffix(overlayFiles[i], "_test.go") | ||||
| 		jTest := strings.HasSuffix(overlayFiles[j], "_test.go") | ||||
| 		if iTest != jTest { | ||||
| 			return !iTest // non-tests are before tests.
 | ||||
| 		} | ||||
| 		return overlayFiles[i] < overlayFiles[j] | ||||
| 	}) | ||||
| 	for _, opath := range overlayFiles { | ||||
| 		contents := state.cfg.Overlay[opath] | ||||
| 		base := filepath.Base(opath) | ||||
| 		dir := filepath.Dir(opath) | ||||
| 		var pkg *Package           // if opath belongs to both a package and its test variant, this will be the test variant
 | ||||
| 		var testVariantOf *Package // if opath is a test file, this is the package it is testing
 | ||||
| 		var fileExists bool | ||||
| 		isTestFile := strings.HasSuffix(opath, "_test.go") | ||||
| 		pkgName, ok := extractPackageName(opath, contents) | ||||
| 		if !ok { | ||||
| 			// Don't bother adding a file that doesn't even have a parsable package statement
 | ||||
| 			// to the overlay.
 | ||||
| 			continue | ||||
| 		} | ||||
| 		// If all the overlay files belong to a different package, change the
 | ||||
| 		// package name to that package.
 | ||||
| 		maybeFixPackageName(pkgName, isTestFile, pkgOfDir[dir]) | ||||
| 	nextPackage: | ||||
| 		for _, p := range response.dr.Packages { | ||||
| 			if pkgName != p.Name && p.ID != "command-line-arguments" { | ||||
| 				continue | ||||
| 			} | ||||
| 			for _, f := range p.GoFiles { | ||||
| 				if !sameFile(filepath.Dir(f), dir) { | ||||
| 					continue | ||||
| 				} | ||||
| 				// Make sure to capture information on the package's test variant, if needed.
 | ||||
| 				if isTestFile && !hasTestFiles(p) { | ||||
| 					// TODO(matloob): Are there packages other than the 'production' variant
 | ||||
| 					// of a package that this can match? This shouldn't match the test main package
 | ||||
| 					// because the file is generated in another directory.
 | ||||
| 					testVariantOf = p | ||||
| 					continue nextPackage | ||||
| 				} else if !isTestFile && hasTestFiles(p) { | ||||
| 					// We're examining a test variant, but the overlaid file is
 | ||||
| 					// a non-test file. Because the overlay implementation
 | ||||
| 					// (currently) only adds a file to one package, skip this
 | ||||
| 					// package, so that we can add the file to the production
 | ||||
| 					// variant of the package. (https://golang.org/issue/36857
 | ||||
| 					// tracks handling overlays on both the production and test
 | ||||
| 					// variant of a package).
 | ||||
| 					continue nextPackage | ||||
| 				} | ||||
| 				if pkg != nil && p != pkg && pkg.PkgPath == p.PkgPath { | ||||
| 					// We have already seen the production version of the
 | ||||
| 					// for which p is a test variant.
 | ||||
| 					if hasTestFiles(p) { | ||||
| 						testVariantOf = pkg | ||||
| 					} | ||||
| 				} | ||||
| 				pkg = p | ||||
| 				if filepath.Base(f) == base { | ||||
| 					fileExists = true | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		// The overlay could have included an entirely new package or an
 | ||||
| 		// ad-hoc package. An ad-hoc package is one that we have manually
 | ||||
| 		// constructed from inadequate `go list` results for a file= query.
 | ||||
| 		// It will have the ID command-line-arguments.
 | ||||
| 		if pkg == nil || pkg.ID == "command-line-arguments" { | ||||
| 			// Try to find the module or gopath dir the file is contained in.
 | ||||
| 			// Then for modules, add the module opath to the beginning.
 | ||||
| 			pkgPath, ok, err := state.getPkgPath(dir) | ||||
| 			if err != nil { | ||||
| 				return nil, nil, err | ||||
| 			} | ||||
| 			if !ok { | ||||
| 				break | ||||
| 			} | ||||
| 			var forTest string // only set for x tests
 | ||||
| 			isXTest := strings.HasSuffix(pkgName, "_test") | ||||
| 			if isXTest { | ||||
| 				forTest = pkgPath | ||||
| 				pkgPath += "_test" | ||||
| 			} | ||||
| 			id := pkgPath | ||||
| 			if isTestFile { | ||||
| 				if isXTest { | ||||
| 					id = fmt.Sprintf("%s [%s.test]", pkgPath, forTest) | ||||
| 				} else { | ||||
| 					id = fmt.Sprintf("%s [%s.test]", pkgPath, pkgPath) | ||||
| 				} | ||||
| 			} | ||||
| 			if pkg != nil { | ||||
| 				// TODO(rstambler): We should change the package's path and ID
 | ||||
| 				// here. The only issue is that this messes with the roots.
 | ||||
| 			} else { | ||||
| 				// Try to reclaim a package with the same ID, if it exists in the response.
 | ||||
| 				for _, p := range response.dr.Packages { | ||||
| 					if reclaimPackage(p, id, opath, contents) { | ||||
| 						pkg = p | ||||
| 						break | ||||
| 					} | ||||
| 				} | ||||
| 				// Otherwise, create a new package.
 | ||||
| 				if pkg == nil { | ||||
| 					pkg = &Package{ | ||||
| 						PkgPath: pkgPath, | ||||
| 						ID:      id, | ||||
| 						Name:    pkgName, | ||||
| 						Imports: make(map[string]*Package), | ||||
| 					} | ||||
| 					response.addPackage(pkg) | ||||
| 					havePkgs[pkg.PkgPath] = id | ||||
| 					// Add the production package's sources for a test variant.
 | ||||
| 					if isTestFile && !isXTest && testVariantOf != nil { | ||||
| 						pkg.GoFiles = append(pkg.GoFiles, testVariantOf.GoFiles...) | ||||
| 						pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, testVariantOf.CompiledGoFiles...) | ||||
| 						// Add the package under test and its imports to the test variant.
 | ||||
| 						pkg.forTest = testVariantOf.PkgPath | ||||
| 						for k, v := range testVariantOf.Imports { | ||||
| 							pkg.Imports[k] = &Package{ID: v.ID} | ||||
| 						} | ||||
| 					} | ||||
| 					if isXTest { | ||||
| 						pkg.forTest = forTest | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		if !fileExists { | ||||
| 			pkg.GoFiles = append(pkg.GoFiles, opath) | ||||
| 			// TODO(matloob): Adding the file to CompiledGoFiles can exhibit the wrong behavior
 | ||||
| 			// if the file will be ignored due to its build tags.
 | ||||
| 			pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, opath) | ||||
| 			modifiedPkgsSet[pkg.ID] = true | ||||
| 		} | ||||
| 		imports, err := extractImports(opath, contents) | ||||
| 		if err != nil { | ||||
| 			// Let the parser or type checker report errors later.
 | ||||
| 			continue | ||||
| 		} | ||||
| 		for _, imp := range imports { | ||||
| 			// TODO(rstambler): If the package is an x test and the import has
 | ||||
| 			// a test variant, make sure to replace it.
 | ||||
| 			if _, found := pkg.Imports[imp]; found { | ||||
| 				continue | ||||
| 			} | ||||
| 			overlayAddsImports = true | ||||
| 			id, ok := havePkgs[imp] | ||||
| 			if !ok { | ||||
| 				var err error | ||||
| 				id, err = state.resolveImport(dir, imp) | ||||
| 				if err != nil { | ||||
| 					return nil, nil, err | ||||
| 				} | ||||
| 			} | ||||
| 			pkg.Imports[imp] = &Package{ID: id} | ||||
| 			// Add dependencies to the non-test variant version of this package as well.
 | ||||
| 			if testVariantOf != nil { | ||||
| 				testVariantOf.Imports[imp] = &Package{ID: id} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// toPkgPath guesses the package path given the id.
 | ||||
| 	toPkgPath := func(sourceDir, id string) (string, error) { | ||||
| 		if i := strings.IndexByte(id, ' '); i >= 0 { | ||||
| 			return state.resolveImport(sourceDir, id[:i]) | ||||
| 		} | ||||
| 		return state.resolveImport(sourceDir, id) | ||||
| 	} | ||||
| 
 | ||||
| 	// Now that new packages have been created, do another pass to determine
 | ||||
| 	// the new set of missing packages.
 | ||||
| 	for _, pkg := range response.dr.Packages { | ||||
| 		for _, imp := range pkg.Imports { | ||||
| 			if len(pkg.GoFiles) == 0 { | ||||
| 				return nil, nil, fmt.Errorf("cannot resolve imports for package %q with no Go files", pkg.PkgPath) | ||||
| 			} | ||||
| 			pkgPath, err := toPkgPath(filepath.Dir(pkg.GoFiles[0]), imp.ID) | ||||
| 			if err != nil { | ||||
| 				return nil, nil, err | ||||
| 			} | ||||
| 			if _, ok := havePkgs[pkgPath]; !ok { | ||||
| 				needPkgsSet[pkgPath] = true | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if overlayAddsImports { | ||||
| 		needPkgs = make([]string, 0, len(needPkgsSet)) | ||||
| 		for pkg := range needPkgsSet { | ||||
| 			needPkgs = append(needPkgs, pkg) | ||||
| 		} | ||||
| 	} | ||||
| 	modifiedPkgs = make([]string, 0, len(modifiedPkgsSet)) | ||||
| 	for pkg := range modifiedPkgsSet { | ||||
| 		modifiedPkgs = append(modifiedPkgs, pkg) | ||||
| 	} | ||||
| 	return modifiedPkgs, needPkgs, err | ||||
| } | ||||
| 
 | ||||
| // resolveImport finds the ID of a package given its import path.
 | ||||
| // In particular, it will find the right vendored copy when in GOPATH mode.
 | ||||
| func (state *golistState) resolveImport(sourceDir, importPath string) (string, error) { | ||||
| 	env, err := state.getEnv() | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	if env["GOMOD"] != "" { | ||||
| 		return importPath, nil | ||||
| 	} | ||||
| 
 | ||||
| 	searchDir := sourceDir | ||||
| 	for { | ||||
| 		vendorDir := filepath.Join(searchDir, "vendor") | ||||
| 		exists, ok := state.vendorDirs[vendorDir] | ||||
| 		if !ok { | ||||
| 			info, err := os.Stat(vendorDir) | ||||
| 			exists = err == nil && info.IsDir() | ||||
| 			state.vendorDirs[vendorDir] = exists | ||||
| 		} | ||||
| 
 | ||||
| 		if exists { | ||||
| 			vendoredPath := filepath.Join(vendorDir, importPath) | ||||
| 			if info, err := os.Stat(vendoredPath); err == nil && info.IsDir() { | ||||
| 				// We should probably check for .go files here, but shame on anyone who fools us.
 | ||||
| 				path, ok, err := state.getPkgPath(vendoredPath) | ||||
| 				if err != nil { | ||||
| 					return "", err | ||||
| 				} | ||||
| 				if ok { | ||||
| 					return path, nil | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// We know we've hit the top of the filesystem when we Dir / and get /,
 | ||||
| 		// or C:\ and get C:\, etc.
 | ||||
| 		next := filepath.Dir(searchDir) | ||||
| 		if next == searchDir { | ||||
| 			break | ||||
| 		} | ||||
| 		searchDir = next | ||||
| 	} | ||||
| 	return importPath, nil | ||||
| } | ||||
| 
 | ||||
| func hasTestFiles(p *Package) bool { | ||||
| 	for _, f := range p.GoFiles { | ||||
| 		if strings.HasSuffix(f, "_test.go") { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| // determineRootDirs returns a mapping from absolute directories that could
 | ||||
| // contain code to their corresponding import path prefixes.
 | ||||
| func (state *golistState) determineRootDirs() (map[string]string, error) { | ||||
| 	env, err := state.getEnv() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if env["GOMOD"] != "" { | ||||
| 		state.rootsOnce.Do(func() { | ||||
| 			state.rootDirs, state.rootDirsError = state.determineRootDirsModules() | ||||
| 		}) | ||||
| 	} else { | ||||
| 		state.rootsOnce.Do(func() { | ||||
| 			state.rootDirs, state.rootDirsError = state.determineRootDirsGOPATH() | ||||
| 		}) | ||||
| 	} | ||||
| 	return state.rootDirs, state.rootDirsError | ||||
| } | ||||
| 
 | ||||
| func (state *golistState) determineRootDirsModules() (map[string]string, error) { | ||||
| 	// List all of the modules--the first will be the directory for the main
 | ||||
| 	// module. Any replaced modules will also need to be treated as roots.
 | ||||
| 	// Editing files in the module cache isn't a great idea, so we don't
 | ||||
| 	// plan to ever support that.
 | ||||
| 	out, err := state.invokeGo("list", "-m", "-json", "all") | ||||
| 	if err != nil { | ||||
| 		// 'go list all' will fail if we're outside of a module and
 | ||||
| 		// GO111MODULE=on. Try falling back without 'all'.
 | ||||
| 		var innerErr error | ||||
| 		out, innerErr = state.invokeGo("list", "-m", "-json") | ||||
| 		if innerErr != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
| 	roots := map[string]string{} | ||||
| 	modules := map[string]string{} | ||||
| 	var i int | ||||
| 	for dec := json.NewDecoder(out); dec.More(); { | ||||
| 		mod := new(gocommand.ModuleJSON) | ||||
| 		if err := dec.Decode(mod); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		if mod.Dir != "" && mod.Path != "" { | ||||
| 			// This is a valid module; add it to the map.
 | ||||
| 			absDir, err := filepath.Abs(mod.Dir) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			modules[absDir] = mod.Path | ||||
| 			// The first result is the main module.
 | ||||
| 			if i == 0 || mod.Replace != nil && mod.Replace.Path != "" { | ||||
| 				roots[absDir] = mod.Path | ||||
| 			} | ||||
| 		} | ||||
| 		i++ | ||||
| 	} | ||||
| 	return roots, nil | ||||
| } | ||||
| 
 | ||||
| func (state *golistState) determineRootDirsGOPATH() (map[string]string, error) { | ||||
| 	m := map[string]string{} | ||||
| 	for _, dir := range filepath.SplitList(state.mustGetEnv()["GOPATH"]) { | ||||
| 		absDir, err := filepath.Abs(dir) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		m[filepath.Join(absDir, "src")] = "" | ||||
| 	} | ||||
| 	return m, nil | ||||
| } | ||||
| 
 | ||||
| func extractImports(filename string, contents []byte) ([]string, error) { | ||||
| 	f, err := parser.ParseFile(token.NewFileSet(), filename, contents, parser.ImportsOnly) // TODO(matloob): reuse fileset?
 | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	var res []string | ||||
| 	for _, imp := range f.Imports { | ||||
| 		quotedPath := imp.Path.Value | ||||
| 		path, err := strconv.Unquote(quotedPath) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		res = append(res, path) | ||||
| 	} | ||||
| 	return res, nil | ||||
| } | ||||
| 
 | ||||
| // reclaimPackage attempts to reuse a package that failed to load in an overlay.
 | ||||
| //
 | ||||
| // If the package has errors and has no Name, GoFiles, or Imports,
 | ||||
| // then it's possible that it doesn't yet exist on disk.
 | ||||
| func reclaimPackage(pkg *Package, id string, filename string, contents []byte) bool { | ||||
| 	// TODO(rstambler): Check the message of the actual error?
 | ||||
| 	// It differs between $GOPATH and module mode.
 | ||||
| 	if pkg.ID != id { | ||||
| 		return false | ||||
| 	} | ||||
| 	if len(pkg.Errors) != 1 { | ||||
| 		return false | ||||
| 	} | ||||
| 	if pkg.Name != "" || pkg.ExportFile != "" { | ||||
| 		return false | ||||
| 	} | ||||
| 	if len(pkg.GoFiles) > 0 || len(pkg.CompiledGoFiles) > 0 || len(pkg.OtherFiles) > 0 { | ||||
| 		return false | ||||
| 	} | ||||
| 	if len(pkg.Imports) > 0 { | ||||
| 		return false | ||||
| 	} | ||||
| 	pkgName, ok := extractPackageName(filename, contents) | ||||
| 	if !ok { | ||||
| 		return false | ||||
| 	} | ||||
| 	pkg.Name = pkgName | ||||
| 	pkg.Errors = nil | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func extractPackageName(filename string, contents []byte) (string, bool) { | ||||
| 	// TODO(rstambler): Check the message of the actual error?
 | ||||
| 	// It differs between $GOPATH and module mode.
 | ||||
| 	f, err := parser.ParseFile(token.NewFileSet(), filename, contents, parser.PackageClauseOnly) // TODO(matloob): reuse fileset?
 | ||||
| 	if err != nil { | ||||
| 		return "", false | ||||
| 	} | ||||
| 	return f.Name.Name, true | ||||
| } | ||||
| 
 | ||||
| // commonDir returns the directory that all files are in, "" if files is empty,
 | ||||
| // or an error if they aren't in the same directory.
 | ||||
| func commonDir(files []string) (string, error) { | ||||
| 	seen := make(map[string]bool) | ||||
| 	for _, f := range files { | ||||
| 		seen[filepath.Dir(f)] = true | ||||
| 	} | ||||
| 	if len(seen) > 1 { | ||||
| 		return "", fmt.Errorf("files (%v) are in more than one directory: %v", files, seen) | ||||
| 	} | ||||
| 	for k := range seen { | ||||
| 		// seen has only one element; return it.
 | ||||
| 		return k, nil | ||||
| 	} | ||||
| 	return "", nil // no files
 | ||||
| } | ||||
| 
 | ||||
| // It is possible that the files in the disk directory dir have a different package
 | ||||
| // name from newName, which is deduced from the overlays. If they all have a different
 | ||||
| // package name, and they all have the same package name, then that name becomes
 | ||||
| // the package name.
 | ||||
| // It returns true if it changes the package name, false otherwise.
 | ||||
| func maybeFixPackageName(newName string, isTestFile bool, pkgsOfDir []*Package) { | ||||
| 	names := make(map[string]int) | ||||
| 	for _, p := range pkgsOfDir { | ||||
| 		names[p.Name]++ | ||||
| 	} | ||||
| 	if len(names) != 1 { | ||||
| 		// some files are in different packages
 | ||||
| 		return | ||||
| 	} | ||||
| 	var oldName string | ||||
| 	for k := range names { | ||||
| 		oldName = k | ||||
| 	} | ||||
| 	if newName == oldName { | ||||
| 		return | ||||
| 	} | ||||
| 	// We might have a case where all of the package names in the directory are
 | ||||
| 	// the same, but the overlay file is for an x test, which belongs to its
 | ||||
| 	// own package. If the x test does not yet exist on disk, we may not yet
 | ||||
| 	// have its package name on disk, but we should not rename the packages.
 | ||||
| 	//
 | ||||
| 	// We use a heuristic to determine if this file belongs to an x test:
 | ||||
| 	// The test file should have a package name whose package name has a _test
 | ||||
| 	// suffix or looks like "newName_test".
 | ||||
| 	maybeXTest := strings.HasPrefix(oldName+"_test", newName) || strings.HasSuffix(newName, "_test") | ||||
| 	if isTestFile && maybeXTest { | ||||
| 		return | ||||
| 	} | ||||
| 	for _, p := range pkgsOfDir { | ||||
| 		p.Name = newName | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // This function is copy-pasted from
 | ||||
| // https://github.com/golang/go/blob/9706f510a5e2754595d716bd64be8375997311fb/src/cmd/go/internal/search/search.go#L360.
 | ||||
| // It should be deleted when we remove support for overlays from go/packages.
 | ||||
| //
 | ||||
| // NOTE: This does not handle any ./... or ./ style queries, as this function
 | ||||
| // doesn't know the working directory.
 | ||||
| //
 | ||||
| // matchPattern(pattern)(name) reports whether
 | ||||
| // name matches pattern. Pattern is a limited glob
 | ||||
| // pattern in which '...' means 'any string' and there
 | ||||
| // is no other special syntax.
 | ||||
| // Unfortunately, there are two special cases. Quoting "go help packages":
 | ||||
| //
 | ||||
| // First, /... at the end of the pattern can match an empty string,
 | ||||
| // so that net/... matches both net and packages in its subdirectories, like net/http.
 | ||||
| // Second, any slash-separated pattern element containing a wildcard never
 | ||||
| // participates in a match of the "vendor" element in the path of a vendored
 | ||||
| // package, so that ./... does not match packages in subdirectories of
 | ||||
| // ./vendor or ./mycode/vendor, but ./vendor/... and ./mycode/vendor/... do.
 | ||||
| // Note, however, that a directory named vendor that itself contains code
 | ||||
| // is not a vendored package: cmd/vendor would be a command named vendor,
 | ||||
| // and the pattern cmd/... matches it.
 | ||||
| func matchPattern(pattern string) func(name string) bool { | ||||
| 	// Convert pattern to regular expression.
 | ||||
| 	// The strategy for the trailing /... is to nest it in an explicit ? expression.
 | ||||
| 	// The strategy for the vendor exclusion is to change the unmatchable
 | ||||
| 	// vendor strings to a disallowed code point (vendorChar) and to use
 | ||||
| 	// "(anything but that codepoint)*" as the implementation of the ... wildcard.
 | ||||
| 	// This is a bit complicated but the obvious alternative,
 | ||||
| 	// namely a hand-written search like in most shell glob matchers,
 | ||||
| 	// is too easy to make accidentally exponential.
 | ||||
| 	// Using package regexp guarantees linear-time matching.
 | ||||
| 
 | ||||
| 	const vendorChar = "\x00" | ||||
| 
 | ||||
| 	if strings.Contains(pattern, vendorChar) { | ||||
| 		return func(name string) bool { return false } | ||||
| 	} | ||||
| 
 | ||||
| 	re := regexp.QuoteMeta(pattern) | ||||
| 	re = replaceVendor(re, vendorChar) | ||||
| 	switch { | ||||
| 	case strings.HasSuffix(re, `/`+vendorChar+`/\.\.\.`): | ||||
| 		re = strings.TrimSuffix(re, `/`+vendorChar+`/\.\.\.`) + `(/vendor|/` + vendorChar + `/\.\.\.)` | ||||
| 	case re == vendorChar+`/\.\.\.`: | ||||
| 		re = `(/vendor|/` + vendorChar + `/\.\.\.)` | ||||
| 	case strings.HasSuffix(re, `/\.\.\.`): | ||||
| 		re = strings.TrimSuffix(re, `/\.\.\.`) + `(/\.\.\.)?` | ||||
| 	} | ||||
| 	re = strings.ReplaceAll(re, `\.\.\.`, `[^`+vendorChar+`]*`) | ||||
| 
 | ||||
| 	reg := regexp.MustCompile(`^` + re + `$`) | ||||
| 
 | ||||
| 	return func(name string) bool { | ||||
| 		if strings.Contains(name, vendorChar) { | ||||
| 			return false | ||||
| 		} | ||||
| 		return reg.MatchString(replaceVendor(name, vendorChar)) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // replaceVendor returns the result of replacing
 | ||||
| // non-trailing vendor path elements in x with repl.
 | ||||
| func replaceVendor(x, repl string) string { | ||||
| 	if !strings.Contains(x, "vendor") { | ||||
| 		return x | ||||
| 	} | ||||
| 	elem := strings.Split(x, "/") | ||||
| 	for i := 0; i < len(elem)-1; i++ { | ||||
| 		if elem[i] == "vendor" { | ||||
| 			elem[i] = repl | ||||
| 		} | ||||
| 	} | ||||
| 	return strings.Join(elem, "/") | ||||
| } | ||||
|  | @ -0,0 +1,57 @@ | |||
| // Copyright 2019 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.
 | ||||
| 
 | ||||
| package packages | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| var allModes = []LoadMode{ | ||||
| 	NeedName, | ||||
| 	NeedFiles, | ||||
| 	NeedCompiledGoFiles, | ||||
| 	NeedImports, | ||||
| 	NeedDeps, | ||||
| 	NeedExportFile, | ||||
| 	NeedTypes, | ||||
| 	NeedSyntax, | ||||
| 	NeedTypesInfo, | ||||
| 	NeedTypesSizes, | ||||
| } | ||||
| 
 | ||||
| var modeStrings = []string{ | ||||
| 	"NeedName", | ||||
| 	"NeedFiles", | ||||
| 	"NeedCompiledGoFiles", | ||||
| 	"NeedImports", | ||||
| 	"NeedDeps", | ||||
| 	"NeedExportFile", | ||||
| 	"NeedTypes", | ||||
| 	"NeedSyntax", | ||||
| 	"NeedTypesInfo", | ||||
| 	"NeedTypesSizes", | ||||
| } | ||||
| 
 | ||||
| func (mod LoadMode) String() string { | ||||
| 	m := mod | ||||
| 	if m == 0 { | ||||
| 		return "LoadMode(0)" | ||||
| 	} | ||||
| 	var out []string | ||||
| 	for i, x := range allModes { | ||||
| 		if x > m { | ||||
| 			break | ||||
| 		} | ||||
| 		if (m & x) != 0 { | ||||
| 			out = append(out, modeStrings[i]) | ||||
| 			m = m ^ x | ||||
| 		} | ||||
| 	} | ||||
| 	if m != 0 { | ||||
| 		out = append(out, "Unknown") | ||||
| 	} | ||||
| 	return fmt.Sprintf("LoadMode(%s)", strings.Join(out, "|")) | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,59 @@ | |||
| // Copyright 2018 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.
 | ||||
| 
 | ||||
| package packages | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"sort" | ||||
| ) | ||||
| 
 | ||||
| // Visit visits all the packages in the import graph whose roots are
 | ||||
| // pkgs, calling the optional pre function the first time each package
 | ||||
| // is encountered (preorder), and the optional post function after a
 | ||||
| // package's dependencies have been visited (postorder).
 | ||||
| // The boolean result of pre(pkg) determines whether
 | ||||
| // the imports of package pkg are visited.
 | ||||
| func Visit(pkgs []*Package, pre func(*Package) bool, post func(*Package)) { | ||||
| 	seen := make(map[*Package]bool) | ||||
| 	var visit func(*Package) | ||||
| 	visit = func(pkg *Package) { | ||||
| 		if !seen[pkg] { | ||||
| 			seen[pkg] = true | ||||
| 
 | ||||
| 			if pre == nil || pre(pkg) { | ||||
| 				paths := make([]string, 0, len(pkg.Imports)) | ||||
| 				for path := range pkg.Imports { | ||||
| 					paths = append(paths, path) | ||||
| 				} | ||||
| 				sort.Strings(paths) // Imports is a map, this makes visit stable
 | ||||
| 				for _, path := range paths { | ||||
| 					visit(pkg.Imports[path]) | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			if post != nil { | ||||
| 				post(pkg) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	for _, pkg := range pkgs { | ||||
| 		visit(pkg) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // PrintErrors prints to os.Stderr the accumulated errors of all
 | ||||
| // packages in the import graph rooted at pkgs, dependencies first.
 | ||||
| // PrintErrors returns the number of errors printed.
 | ||||
| func PrintErrors(pkgs []*Package) int { | ||||
| 	var n int | ||||
| 	Visit(pkgs, nil, func(pkg *Package) { | ||||
| 		for _, err := range pkg.Errors { | ||||
| 			fmt.Fprintln(os.Stderr, err) | ||||
| 			n++ | ||||
| 		} | ||||
| 	}) | ||||
| 	return n | ||||
| } | ||||
|  | @ -0,0 +1,85 @@ | |||
| // Copyright 2019 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.
 | ||||
| 
 | ||||
| // Package core provides support for event based telemetry.
 | ||||
| package core | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"golang.org/x/tools/internal/event/label" | ||||
| ) | ||||
| 
 | ||||
| // Event holds the information about an event of note that occurred.
 | ||||
| type Event struct { | ||||
| 	at time.Time | ||||
| 
 | ||||
| 	// As events are often on the stack, storing the first few labels directly
 | ||||
| 	// in the event can avoid an allocation at all for the very common cases of
 | ||||
| 	// simple events.
 | ||||
| 	// The length needs to be large enough to cope with the majority of events
 | ||||
| 	// but no so large as to cause undue stack pressure.
 | ||||
| 	// A log message with two values will use 3 labels (one for each value and
 | ||||
| 	// one for the message itself).
 | ||||
| 
 | ||||
| 	static  [3]label.Label // inline storage for the first few labels
 | ||||
| 	dynamic []label.Label  // dynamically sized storage for remaining labels
 | ||||
| } | ||||
| 
 | ||||
| // eventLabelMap implements label.Map for a the labels of an Event.
 | ||||
| type eventLabelMap struct { | ||||
| 	event Event | ||||
| } | ||||
| 
 | ||||
| func (ev Event) At() time.Time { return ev.at } | ||||
| 
 | ||||
| func (ev Event) Format(f fmt.State, r rune) { | ||||
| 	if !ev.at.IsZero() { | ||||
| 		fmt.Fprint(f, ev.at.Format("2006/01/02 15:04:05 ")) | ||||
| 	} | ||||
| 	for index := 0; ev.Valid(index); index++ { | ||||
| 		if l := ev.Label(index); l.Valid() { | ||||
| 			fmt.Fprintf(f, "\n\t%v", l) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (ev Event) Valid(index int) bool { | ||||
| 	return index >= 0 && index < len(ev.static)+len(ev.dynamic) | ||||
| } | ||||
| 
 | ||||
| func (ev Event) Label(index int) label.Label { | ||||
| 	if index < len(ev.static) { | ||||
| 		return ev.static[index] | ||||
| 	} | ||||
| 	return ev.dynamic[index-len(ev.static)] | ||||
| } | ||||
| 
 | ||||
| func (ev Event) Find(key label.Key) label.Label { | ||||
| 	for _, l := range ev.static { | ||||
| 		if l.Key() == key { | ||||
| 			return l | ||||
| 		} | ||||
| 	} | ||||
| 	for _, l := range ev.dynamic { | ||||
| 		if l.Key() == key { | ||||
| 			return l | ||||
| 		} | ||||
| 	} | ||||
| 	return label.Label{} | ||||
| } | ||||
| 
 | ||||
| func MakeEvent(static [3]label.Label, labels []label.Label) Event { | ||||
| 	return Event{ | ||||
| 		static:  static, | ||||
| 		dynamic: labels, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // CloneEvent event returns a copy of the event with the time adjusted to at.
 | ||||
| func CloneEvent(ev Event, at time.Time) Event { | ||||
| 	ev.at = at | ||||
| 	return ev | ||||
| } | ||||
|  | @ -0,0 +1,70 @@ | |||
| // Copyright 2019 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.
 | ||||
| 
 | ||||
| package core | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"sync/atomic" | ||||
| 	"time" | ||||
| 	"unsafe" | ||||
| 
 | ||||
| 	"golang.org/x/tools/internal/event/label" | ||||
| ) | ||||
| 
 | ||||
| // Exporter is a function that handles events.
 | ||||
| // It may return a modified context and event.
 | ||||
| type Exporter func(context.Context, Event, label.Map) context.Context | ||||
| 
 | ||||
| var ( | ||||
| 	exporter unsafe.Pointer | ||||
| ) | ||||
| 
 | ||||
| // SetExporter sets the global exporter function that handles all events.
 | ||||
| // The exporter is called synchronously from the event call site, so it should
 | ||||
| // return quickly so as not to hold up user code.
 | ||||
| func SetExporter(e Exporter) { | ||||
| 	p := unsafe.Pointer(&e) | ||||
| 	if e == nil { | ||||
| 		// &e is always valid, and so p is always valid, but for the early abort
 | ||||
| 		// of ProcessEvent to be efficient it needs to make the nil check on the
 | ||||
| 		// pointer without having to dereference it, so we make the nil function
 | ||||
| 		// also a nil pointer
 | ||||
| 		p = nil | ||||
| 	} | ||||
| 	atomic.StorePointer(&exporter, p) | ||||
| } | ||||
| 
 | ||||
| // deliver is called to deliver an event to the supplied exporter.
 | ||||
| // it will fill in the time.
 | ||||
| func deliver(ctx context.Context, exporter Exporter, ev Event) context.Context { | ||||
| 	// add the current time to the event
 | ||||
| 	ev.at = time.Now() | ||||
| 	// hand the event off to the current exporter
 | ||||
| 	return exporter(ctx, ev, ev) | ||||
| } | ||||
| 
 | ||||
| // Export is called to deliver an event to the global exporter if set.
 | ||||
| func Export(ctx context.Context, ev Event) context.Context { | ||||
| 	// get the global exporter and abort early if there is not one
 | ||||
| 	exporterPtr := (*Exporter)(atomic.LoadPointer(&exporter)) | ||||
| 	if exporterPtr == nil { | ||||
| 		return ctx | ||||
| 	} | ||||
| 	return deliver(ctx, *exporterPtr, ev) | ||||
| } | ||||
| 
 | ||||
| // ExportPair is called to deliver a start event to the supplied exporter.
 | ||||
| // It also returns a function that will deliver the end event to the same
 | ||||
| // exporter.
 | ||||
| // It will fill in the time.
 | ||||
| func ExportPair(ctx context.Context, begin, end Event) (context.Context, func()) { | ||||
| 	// get the global exporter and abort early if there is not one
 | ||||
| 	exporterPtr := (*Exporter)(atomic.LoadPointer(&exporter)) | ||||
| 	if exporterPtr == nil { | ||||
| 		return ctx, func() {} | ||||
| 	} | ||||
| 	ctx = deliver(ctx, *exporterPtr, begin) | ||||
| 	return ctx, func() { deliver(ctx, *exporterPtr, end) } | ||||
| } | ||||
|  | @ -0,0 +1,77 @@ | |||
| // Copyright 2019 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.
 | ||||
| 
 | ||||
| package core | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 
 | ||||
| 	"golang.org/x/tools/internal/event/keys" | ||||
| 	"golang.org/x/tools/internal/event/label" | ||||
| ) | ||||
| 
 | ||||
| // Log1 takes a message and one label delivers a log event to the exporter.
 | ||||
| // It is a customized version of Print that is faster and does no allocation.
 | ||||
| func Log1(ctx context.Context, message string, t1 label.Label) { | ||||
| 	Export(ctx, MakeEvent([3]label.Label{ | ||||
| 		keys.Msg.Of(message), | ||||
| 		t1, | ||||
| 	}, nil)) | ||||
| } | ||||
| 
 | ||||
| // Log2 takes a message and two labels and delivers a log event to the exporter.
 | ||||
| // It is a customized version of Print that is faster and does no allocation.
 | ||||
| func Log2(ctx context.Context, message string, t1 label.Label, t2 label.Label) { | ||||
| 	Export(ctx, MakeEvent([3]label.Label{ | ||||
| 		keys.Msg.Of(message), | ||||
| 		t1, | ||||
| 		t2, | ||||
| 	}, nil)) | ||||
| } | ||||
| 
 | ||||
| // Metric1 sends a label event to the exporter with the supplied labels.
 | ||||
| func Metric1(ctx context.Context, t1 label.Label) context.Context { | ||||
| 	return Export(ctx, MakeEvent([3]label.Label{ | ||||
| 		keys.Metric.New(), | ||||
| 		t1, | ||||
| 	}, nil)) | ||||
| } | ||||
| 
 | ||||
| // Metric2 sends a label event to the exporter with the supplied labels.
 | ||||
| func Metric2(ctx context.Context, t1, t2 label.Label) context.Context { | ||||
| 	return Export(ctx, MakeEvent([3]label.Label{ | ||||
| 		keys.Metric.New(), | ||||
| 		t1, | ||||
| 		t2, | ||||
| 	}, nil)) | ||||
| } | ||||
| 
 | ||||
| // Start1 sends a span start event with the supplied label list to the exporter.
 | ||||
| // It also returns a function that will end the span, which should normally be
 | ||||
| // deferred.
 | ||||
| func Start1(ctx context.Context, name string, t1 label.Label) (context.Context, func()) { | ||||
| 	return ExportPair(ctx, | ||||
| 		MakeEvent([3]label.Label{ | ||||
| 			keys.Start.Of(name), | ||||
| 			t1, | ||||
| 		}, nil), | ||||
| 		MakeEvent([3]label.Label{ | ||||
| 			keys.End.New(), | ||||
| 		}, nil)) | ||||
| } | ||||
| 
 | ||||
| // Start2 sends a span start event with the supplied label list to the exporter.
 | ||||
| // It also returns a function that will end the span, which should normally be
 | ||||
| // deferred.
 | ||||
| func Start2(ctx context.Context, name string, t1, t2 label.Label) (context.Context, func()) { | ||||
| 	return ExportPair(ctx, | ||||
| 		MakeEvent([3]label.Label{ | ||||
| 			keys.Start.Of(name), | ||||
| 			t1, | ||||
| 			t2, | ||||
| 		}, nil), | ||||
| 		MakeEvent([3]label.Label{ | ||||
| 			keys.End.New(), | ||||
| 		}, nil)) | ||||
| } | ||||
|  | @ -0,0 +1,7 @@ | |||
| // Copyright 2019 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.
 | ||||
| 
 | ||||
| // Package event provides a set of packages that cover the main
 | ||||
| // concepts of telemetry in an implementation agnostic way.
 | ||||
| package event | ||||
|  | @ -0,0 +1,127 @@ | |||
| // Copyright 2019 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.
 | ||||
| 
 | ||||
| package event | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 
 | ||||
| 	"golang.org/x/tools/internal/event/core" | ||||
| 	"golang.org/x/tools/internal/event/keys" | ||||
| 	"golang.org/x/tools/internal/event/label" | ||||
| ) | ||||
| 
 | ||||
| // Exporter is a function that handles events.
 | ||||
| // It may return a modified context and event.
 | ||||
| type Exporter func(context.Context, core.Event, label.Map) context.Context | ||||
| 
 | ||||
| // SetExporter sets the global exporter function that handles all events.
 | ||||
| // The exporter is called synchronously from the event call site, so it should
 | ||||
| // return quickly so as not to hold up user code.
 | ||||
| func SetExporter(e Exporter) { | ||||
| 	core.SetExporter(core.Exporter(e)) | ||||
| } | ||||
| 
 | ||||
| // Log takes a message and a label list and combines them into a single event
 | ||||
| // before delivering them to the exporter.
 | ||||
| func Log(ctx context.Context, message string, labels ...label.Label) { | ||||
| 	core.Export(ctx, core.MakeEvent([3]label.Label{ | ||||
| 		keys.Msg.Of(message), | ||||
| 	}, labels)) | ||||
| } | ||||
| 
 | ||||
| // IsLog returns true if the event was built by the Log function.
 | ||||
| // It is intended to be used in exporters to identify the semantics of the
 | ||||
| // event when deciding what to do with it.
 | ||||
| func IsLog(ev core.Event) bool { | ||||
| 	return ev.Label(0).Key() == keys.Msg | ||||
| } | ||||
| 
 | ||||
| // Error takes a message and a label list and combines them into a single event
 | ||||
| // before delivering them to the exporter. It captures the error in the
 | ||||
| // delivered event.
 | ||||
| func Error(ctx context.Context, message string, err error, labels ...label.Label) { | ||||
| 	core.Export(ctx, core.MakeEvent([3]label.Label{ | ||||
| 		keys.Msg.Of(message), | ||||
| 		keys.Err.Of(err), | ||||
| 	}, labels)) | ||||
| } | ||||
| 
 | ||||
| // IsError returns true if the event was built by the Error function.
 | ||||
| // It is intended to be used in exporters to identify the semantics of the
 | ||||
| // event when deciding what to do with it.
 | ||||
| func IsError(ev core.Event) bool { | ||||
| 	return ev.Label(0).Key() == keys.Msg && | ||||
| 		ev.Label(1).Key() == keys.Err | ||||
| } | ||||
| 
 | ||||
| // Metric sends a label event to the exporter with the supplied labels.
 | ||||
| func Metric(ctx context.Context, labels ...label.Label) { | ||||
| 	core.Export(ctx, core.MakeEvent([3]label.Label{ | ||||
| 		keys.Metric.New(), | ||||
| 	}, labels)) | ||||
| } | ||||
| 
 | ||||
| // IsMetric returns true if the event was built by the Metric function.
 | ||||
| // It is intended to be used in exporters to identify the semantics of the
 | ||||
| // event when deciding what to do with it.
 | ||||
| func IsMetric(ev core.Event) bool { | ||||
| 	return ev.Label(0).Key() == keys.Metric | ||||
| } | ||||
| 
 | ||||
| // Label sends a label event to the exporter with the supplied labels.
 | ||||
| func Label(ctx context.Context, labels ...label.Label) context.Context { | ||||
| 	return core.Export(ctx, core.MakeEvent([3]label.Label{ | ||||
| 		keys.Label.New(), | ||||
| 	}, labels)) | ||||
| } | ||||
| 
 | ||||
| // IsLabel returns true if the event was built by the Label function.
 | ||||
| // It is intended to be used in exporters to identify the semantics of the
 | ||||
| // event when deciding what to do with it.
 | ||||
| func IsLabel(ev core.Event) bool { | ||||
| 	return ev.Label(0).Key() == keys.Label | ||||
| } | ||||
| 
 | ||||
| // Start sends a span start event with the supplied label list to the exporter.
 | ||||
| // It also returns a function that will end the span, which should normally be
 | ||||
| // deferred.
 | ||||
| func Start(ctx context.Context, name string, labels ...label.Label) (context.Context, func()) { | ||||
| 	return core.ExportPair(ctx, | ||||
| 		core.MakeEvent([3]label.Label{ | ||||
| 			keys.Start.Of(name), | ||||
| 		}, labels), | ||||
| 		core.MakeEvent([3]label.Label{ | ||||
| 			keys.End.New(), | ||||
| 		}, nil)) | ||||
| } | ||||
| 
 | ||||
| // IsStart returns true if the event was built by the Start function.
 | ||||
| // It is intended to be used in exporters to identify the semantics of the
 | ||||
| // event when deciding what to do with it.
 | ||||
| func IsStart(ev core.Event) bool { | ||||
| 	return ev.Label(0).Key() == keys.Start | ||||
| } | ||||
| 
 | ||||
| // IsEnd returns true if the event was built by the End function.
 | ||||
| // It is intended to be used in exporters to identify the semantics of the
 | ||||
| // event when deciding what to do with it.
 | ||||
| func IsEnd(ev core.Event) bool { | ||||
| 	return ev.Label(0).Key() == keys.End | ||||
| } | ||||
| 
 | ||||
| // Detach returns a context without an associated span.
 | ||||
| // This allows the creation of spans that are not children of the current span.
 | ||||
| func Detach(ctx context.Context) context.Context { | ||||
| 	return core.Export(ctx, core.MakeEvent([3]label.Label{ | ||||
| 		keys.Detach.New(), | ||||
| 	}, nil)) | ||||
| } | ||||
| 
 | ||||
| // IsDetach returns true if the event was built by the Detach function.
 | ||||
| // It is intended to be used in exporters to identify the semantics of the
 | ||||
| // event when deciding what to do with it.
 | ||||
| func IsDetach(ev core.Event) bool { | ||||
| 	return ev.Label(0).Key() == keys.Detach | ||||
| } | ||||
|  | @ -0,0 +1,564 @@ | |||
| // Copyright 2019 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.
 | ||||
| 
 | ||||
| package keys | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"math" | ||||
| 	"strconv" | ||||
| 
 | ||||
| 	"golang.org/x/tools/internal/event/label" | ||||
| ) | ||||
| 
 | ||||
| // Value represents a key for untyped values.
 | ||||
| type Value struct { | ||||
| 	name        string | ||||
| 	description string | ||||
| } | ||||
| 
 | ||||
| // New creates a new Key for untyped values.
 | ||||
| func New(name, description string) *Value { | ||||
| 	return &Value{name: name, description: description} | ||||
| } | ||||
| 
 | ||||
| func (k *Value) Name() string        { return k.name } | ||||
| func (k *Value) Description() string { return k.description } | ||||
| 
 | ||||
| func (k *Value) Format(w io.Writer, buf []byte, l label.Label) { | ||||
| 	fmt.Fprint(w, k.From(l)) | ||||
| } | ||||
| 
 | ||||
| // Get can be used to get a label for the key from a label.Map.
 | ||||
| func (k *Value) Get(lm label.Map) interface{} { | ||||
| 	if t := lm.Find(k); t.Valid() { | ||||
| 		return k.From(t) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // From can be used to get a value from a Label.
 | ||||
| func (k *Value) From(t label.Label) interface{} { return t.UnpackValue() } | ||||
| 
 | ||||
| // Of creates a new Label with this key and the supplied value.
 | ||||
| func (k *Value) Of(value interface{}) label.Label { return label.OfValue(k, value) } | ||||
| 
 | ||||
| // Tag represents a key for tagging labels that have no value.
 | ||||
| // These are used when the existence of the label is the entire information it
 | ||||
| // carries, such as marking events to be of a specific kind, or from a specific
 | ||||
| // package.
 | ||||
| type Tag struct { | ||||
| 	name        string | ||||
| 	description string | ||||
| } | ||||
| 
 | ||||
| // NewTag creates a new Key for tagging labels.
 | ||||
| func NewTag(name, description string) *Tag { | ||||
| 	return &Tag{name: name, description: description} | ||||
| } | ||||
| 
 | ||||
| func (k *Tag) Name() string        { return k.name } | ||||
| func (k *Tag) Description() string { return k.description } | ||||
| 
 | ||||
| func (k *Tag) Format(w io.Writer, buf []byte, l label.Label) {} | ||||
| 
 | ||||
| // New creates a new Label with this key.
 | ||||
| func (k *Tag) New() label.Label { return label.OfValue(k, nil) } | ||||
| 
 | ||||
| // Int represents a key
 | ||||
| type Int struct { | ||||
| 	name        string | ||||
| 	description string | ||||
| } | ||||
| 
 | ||||
| // NewInt creates a new Key for int values.
 | ||||
| func NewInt(name, description string) *Int { | ||||
| 	return &Int{name: name, description: description} | ||||
| } | ||||
| 
 | ||||
| func (k *Int) Name() string        { return k.name } | ||||
| func (k *Int) Description() string { return k.description } | ||||
| 
 | ||||
| func (k *Int) Format(w io.Writer, buf []byte, l label.Label) { | ||||
| 	w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10)) | ||||
| } | ||||
| 
 | ||||
| // Of creates a new Label with this key and the supplied value.
 | ||||
| func (k *Int) Of(v int) label.Label { return label.Of64(k, uint64(v)) } | ||||
| 
 | ||||
| // Get can be used to get a label for the key from a label.Map.
 | ||||
| func (k *Int) Get(lm label.Map) int { | ||||
| 	if t := lm.Find(k); t.Valid() { | ||||
| 		return k.From(t) | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| // From can be used to get a value from a Label.
 | ||||
| func (k *Int) From(t label.Label) int { return int(t.Unpack64()) } | ||||
| 
 | ||||
| // Int8 represents a key
 | ||||
| type Int8 struct { | ||||
| 	name        string | ||||
| 	description string | ||||
| } | ||||
| 
 | ||||
| // NewInt8 creates a new Key for int8 values.
 | ||||
| func NewInt8(name, description string) *Int8 { | ||||
| 	return &Int8{name: name, description: description} | ||||
| } | ||||
| 
 | ||||
| func (k *Int8) Name() string        { return k.name } | ||||
| func (k *Int8) Description() string { return k.description } | ||||
| 
 | ||||
| func (k *Int8) Format(w io.Writer, buf []byte, l label.Label) { | ||||
| 	w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10)) | ||||
| } | ||||
| 
 | ||||
| // Of creates a new Label with this key and the supplied value.
 | ||||
| func (k *Int8) Of(v int8) label.Label { return label.Of64(k, uint64(v)) } | ||||
| 
 | ||||
| // Get can be used to get a label for the key from a label.Map.
 | ||||
| func (k *Int8) Get(lm label.Map) int8 { | ||||
| 	if t := lm.Find(k); t.Valid() { | ||||
| 		return k.From(t) | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| // From can be used to get a value from a Label.
 | ||||
| func (k *Int8) From(t label.Label) int8 { return int8(t.Unpack64()) } | ||||
| 
 | ||||
| // Int16 represents a key
 | ||||
| type Int16 struct { | ||||
| 	name        string | ||||
| 	description string | ||||
| } | ||||
| 
 | ||||
| // NewInt16 creates a new Key for int16 values.
 | ||||
| func NewInt16(name, description string) *Int16 { | ||||
| 	return &Int16{name: name, description: description} | ||||
| } | ||||
| 
 | ||||
| func (k *Int16) Name() string        { return k.name } | ||||
| func (k *Int16) Description() string { return k.description } | ||||
| 
 | ||||
| func (k *Int16) Format(w io.Writer, buf []byte, l label.Label) { | ||||
| 	w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10)) | ||||
| } | ||||
| 
 | ||||
| // Of creates a new Label with this key and the supplied value.
 | ||||
| func (k *Int16) Of(v int16) label.Label { return label.Of64(k, uint64(v)) } | ||||
| 
 | ||||
| // Get can be used to get a label for the key from a label.Map.
 | ||||
| func (k *Int16) Get(lm label.Map) int16 { | ||||
| 	if t := lm.Find(k); t.Valid() { | ||||
| 		return k.From(t) | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| // From can be used to get a value from a Label.
 | ||||
| func (k *Int16) From(t label.Label) int16 { return int16(t.Unpack64()) } | ||||
| 
 | ||||
| // Int32 represents a key
 | ||||
| type Int32 struct { | ||||
| 	name        string | ||||
| 	description string | ||||
| } | ||||
| 
 | ||||
| // NewInt32 creates a new Key for int32 values.
 | ||||
| func NewInt32(name, description string) *Int32 { | ||||
| 	return &Int32{name: name, description: description} | ||||
| } | ||||
| 
 | ||||
| func (k *Int32) Name() string        { return k.name } | ||||
| func (k *Int32) Description() string { return k.description } | ||||
| 
 | ||||
| func (k *Int32) Format(w io.Writer, buf []byte, l label.Label) { | ||||
| 	w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10)) | ||||
| } | ||||
| 
 | ||||
| // Of creates a new Label with this key and the supplied value.
 | ||||
| func (k *Int32) Of(v int32) label.Label { return label.Of64(k, uint64(v)) } | ||||
| 
 | ||||
| // Get can be used to get a label for the key from a label.Map.
 | ||||
| func (k *Int32) Get(lm label.Map) int32 { | ||||
| 	if t := lm.Find(k); t.Valid() { | ||||
| 		return k.From(t) | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| // From can be used to get a value from a Label.
 | ||||
| func (k *Int32) From(t label.Label) int32 { return int32(t.Unpack64()) } | ||||
| 
 | ||||
| // Int64 represents a key
 | ||||
| type Int64 struct { | ||||
| 	name        string | ||||
| 	description string | ||||
| } | ||||
| 
 | ||||
| // NewInt64 creates a new Key for int64 values.
 | ||||
| func NewInt64(name, description string) *Int64 { | ||||
| 	return &Int64{name: name, description: description} | ||||
| } | ||||
| 
 | ||||
| func (k *Int64) Name() string        { return k.name } | ||||
| func (k *Int64) Description() string { return k.description } | ||||
| 
 | ||||
| func (k *Int64) Format(w io.Writer, buf []byte, l label.Label) { | ||||
| 	w.Write(strconv.AppendInt(buf, k.From(l), 10)) | ||||
| } | ||||
| 
 | ||||
| // Of creates a new Label with this key and the supplied value.
 | ||||
| func (k *Int64) Of(v int64) label.Label { return label.Of64(k, uint64(v)) } | ||||
| 
 | ||||
| // Get can be used to get a label for the key from a label.Map.
 | ||||
| func (k *Int64) Get(lm label.Map) int64 { | ||||
| 	if t := lm.Find(k); t.Valid() { | ||||
| 		return k.From(t) | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| // From can be used to get a value from a Label.
 | ||||
| func (k *Int64) From(t label.Label) int64 { return int64(t.Unpack64()) } | ||||
| 
 | ||||
| // UInt represents a key
 | ||||
| type UInt struct { | ||||
| 	name        string | ||||
| 	description string | ||||
| } | ||||
| 
 | ||||
| // NewUInt creates a new Key for uint values.
 | ||||
| func NewUInt(name, description string) *UInt { | ||||
| 	return &UInt{name: name, description: description} | ||||
| } | ||||
| 
 | ||||
| func (k *UInt) Name() string        { return k.name } | ||||
| func (k *UInt) Description() string { return k.description } | ||||
| 
 | ||||
| func (k *UInt) Format(w io.Writer, buf []byte, l label.Label) { | ||||
| 	w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10)) | ||||
| } | ||||
| 
 | ||||
| // Of creates a new Label with this key and the supplied value.
 | ||||
| func (k *UInt) Of(v uint) label.Label { return label.Of64(k, uint64(v)) } | ||||
| 
 | ||||
| // Get can be used to get a label for the key from a label.Map.
 | ||||
| func (k *UInt) Get(lm label.Map) uint { | ||||
| 	if t := lm.Find(k); t.Valid() { | ||||
| 		return k.From(t) | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| // From can be used to get a value from a Label.
 | ||||
| func (k *UInt) From(t label.Label) uint { return uint(t.Unpack64()) } | ||||
| 
 | ||||
| // UInt8 represents a key
 | ||||
| type UInt8 struct { | ||||
| 	name        string | ||||
| 	description string | ||||
| } | ||||
| 
 | ||||
| // NewUInt8 creates a new Key for uint8 values.
 | ||||
| func NewUInt8(name, description string) *UInt8 { | ||||
| 	return &UInt8{name: name, description: description} | ||||
| } | ||||
| 
 | ||||
| func (k *UInt8) Name() string        { return k.name } | ||||
| func (k *UInt8) Description() string { return k.description } | ||||
| 
 | ||||
| func (k *UInt8) Format(w io.Writer, buf []byte, l label.Label) { | ||||
| 	w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10)) | ||||
| } | ||||
| 
 | ||||
| // Of creates a new Label with this key and the supplied value.
 | ||||
| func (k *UInt8) Of(v uint8) label.Label { return label.Of64(k, uint64(v)) } | ||||
| 
 | ||||
| // Get can be used to get a label for the key from a label.Map.
 | ||||
| func (k *UInt8) Get(lm label.Map) uint8 { | ||||
| 	if t := lm.Find(k); t.Valid() { | ||||
| 		return k.From(t) | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| // From can be used to get a value from a Label.
 | ||||
| func (k *UInt8) From(t label.Label) uint8 { return uint8(t.Unpack64()) } | ||||
| 
 | ||||
| // UInt16 represents a key
 | ||||
| type UInt16 struct { | ||||
| 	name        string | ||||
| 	description string | ||||
| } | ||||
| 
 | ||||
| // NewUInt16 creates a new Key for uint16 values.
 | ||||
| func NewUInt16(name, description string) *UInt16 { | ||||
| 	return &UInt16{name: name, description: description} | ||||
| } | ||||
| 
 | ||||
| func (k *UInt16) Name() string        { return k.name } | ||||
| func (k *UInt16) Description() string { return k.description } | ||||
| 
 | ||||
| func (k *UInt16) Format(w io.Writer, buf []byte, l label.Label) { | ||||
| 	w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10)) | ||||
| } | ||||
| 
 | ||||
| // Of creates a new Label with this key and the supplied value.
 | ||||
| func (k *UInt16) Of(v uint16) label.Label { return label.Of64(k, uint64(v)) } | ||||
| 
 | ||||
| // Get can be used to get a label for the key from a label.Map.
 | ||||
| func (k *UInt16) Get(lm label.Map) uint16 { | ||||
| 	if t := lm.Find(k); t.Valid() { | ||||
| 		return k.From(t) | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| // From can be used to get a value from a Label.
 | ||||
| func (k *UInt16) From(t label.Label) uint16 { return uint16(t.Unpack64()) } | ||||
| 
 | ||||
| // UInt32 represents a key
 | ||||
| type UInt32 struct { | ||||
| 	name        string | ||||
| 	description string | ||||
| } | ||||
| 
 | ||||
| // NewUInt32 creates a new Key for uint32 values.
 | ||||
| func NewUInt32(name, description string) *UInt32 { | ||||
| 	return &UInt32{name: name, description: description} | ||||
| } | ||||
| 
 | ||||
| func (k *UInt32) Name() string        { return k.name } | ||||
| func (k *UInt32) Description() string { return k.description } | ||||
| 
 | ||||
| func (k *UInt32) Format(w io.Writer, buf []byte, l label.Label) { | ||||
| 	w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10)) | ||||
| } | ||||
| 
 | ||||
| // Of creates a new Label with this key and the supplied value.
 | ||||
| func (k *UInt32) Of(v uint32) label.Label { return label.Of64(k, uint64(v)) } | ||||
| 
 | ||||
| // Get can be used to get a label for the key from a label.Map.
 | ||||
| func (k *UInt32) Get(lm label.Map) uint32 { | ||||
| 	if t := lm.Find(k); t.Valid() { | ||||
| 		return k.From(t) | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| // From can be used to get a value from a Label.
 | ||||
| func (k *UInt32) From(t label.Label) uint32 { return uint32(t.Unpack64()) } | ||||
| 
 | ||||
| // UInt64 represents a key
 | ||||
| type UInt64 struct { | ||||
| 	name        string | ||||
| 	description string | ||||
| } | ||||
| 
 | ||||
| // NewUInt64 creates a new Key for uint64 values.
 | ||||
| func NewUInt64(name, description string) *UInt64 { | ||||
| 	return &UInt64{name: name, description: description} | ||||
| } | ||||
| 
 | ||||
| func (k *UInt64) Name() string        { return k.name } | ||||
| func (k *UInt64) Description() string { return k.description } | ||||
| 
 | ||||
| func (k *UInt64) Format(w io.Writer, buf []byte, l label.Label) { | ||||
| 	w.Write(strconv.AppendUint(buf, k.From(l), 10)) | ||||
| } | ||||
| 
 | ||||
| // Of creates a new Label with this key and the supplied value.
 | ||||
| func (k *UInt64) Of(v uint64) label.Label { return label.Of64(k, v) } | ||||
| 
 | ||||
| // Get can be used to get a label for the key from a label.Map.
 | ||||
| func (k *UInt64) Get(lm label.Map) uint64 { | ||||
| 	if t := lm.Find(k); t.Valid() { | ||||
| 		return k.From(t) | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| // From can be used to get a value from a Label.
 | ||||
| func (k *UInt64) From(t label.Label) uint64 { return t.Unpack64() } | ||||
| 
 | ||||
| // Float32 represents a key
 | ||||
| type Float32 struct { | ||||
| 	name        string | ||||
| 	description string | ||||
| } | ||||
| 
 | ||||
| // NewFloat32 creates a new Key for float32 values.
 | ||||
| func NewFloat32(name, description string) *Float32 { | ||||
| 	return &Float32{name: name, description: description} | ||||
| } | ||||
| 
 | ||||
| func (k *Float32) Name() string        { return k.name } | ||||
| func (k *Float32) Description() string { return k.description } | ||||
| 
 | ||||
| func (k *Float32) Format(w io.Writer, buf []byte, l label.Label) { | ||||
| 	w.Write(strconv.AppendFloat(buf, float64(k.From(l)), 'E', -1, 32)) | ||||
| } | ||||
| 
 | ||||
| // Of creates a new Label with this key and the supplied value.
 | ||||
| func (k *Float32) Of(v float32) label.Label { | ||||
| 	return label.Of64(k, uint64(math.Float32bits(v))) | ||||
| } | ||||
| 
 | ||||
| // Get can be used to get a label for the key from a label.Map.
 | ||||
| func (k *Float32) Get(lm label.Map) float32 { | ||||
| 	if t := lm.Find(k); t.Valid() { | ||||
| 		return k.From(t) | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| // From can be used to get a value from a Label.
 | ||||
| func (k *Float32) From(t label.Label) float32 { | ||||
| 	return math.Float32frombits(uint32(t.Unpack64())) | ||||
| } | ||||
| 
 | ||||
| // Float64 represents a key
 | ||||
| type Float64 struct { | ||||
| 	name        string | ||||
| 	description string | ||||
| } | ||||
| 
 | ||||
| // NewFloat64 creates a new Key for int64 values.
 | ||||
| func NewFloat64(name, description string) *Float64 { | ||||
| 	return &Float64{name: name, description: description} | ||||
| } | ||||
| 
 | ||||
| func (k *Float64) Name() string        { return k.name } | ||||
| func (k *Float64) Description() string { return k.description } | ||||
| 
 | ||||
| func (k *Float64) Format(w io.Writer, buf []byte, l label.Label) { | ||||
| 	w.Write(strconv.AppendFloat(buf, k.From(l), 'E', -1, 64)) | ||||
| } | ||||
| 
 | ||||
| // Of creates a new Label with this key and the supplied value.
 | ||||
| func (k *Float64) Of(v float64) label.Label { | ||||
| 	return label.Of64(k, math.Float64bits(v)) | ||||
| } | ||||
| 
 | ||||
| // Get can be used to get a label for the key from a label.Map.
 | ||||
| func (k *Float64) Get(lm label.Map) float64 { | ||||
| 	if t := lm.Find(k); t.Valid() { | ||||
| 		return k.From(t) | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| // From can be used to get a value from a Label.
 | ||||
| func (k *Float64) From(t label.Label) float64 { | ||||
| 	return math.Float64frombits(t.Unpack64()) | ||||
| } | ||||
| 
 | ||||
| // String represents a key
 | ||||
| type String struct { | ||||
| 	name        string | ||||
| 	description string | ||||
| } | ||||
| 
 | ||||
| // NewString creates a new Key for int64 values.
 | ||||
| func NewString(name, description string) *String { | ||||
| 	return &String{name: name, description: description} | ||||
| } | ||||
| 
 | ||||
| func (k *String) Name() string        { return k.name } | ||||
| func (k *String) Description() string { return k.description } | ||||
| 
 | ||||
| func (k *String) Format(w io.Writer, buf []byte, l label.Label) { | ||||
| 	w.Write(strconv.AppendQuote(buf, k.From(l))) | ||||
| } | ||||
| 
 | ||||
| // Of creates a new Label with this key and the supplied value.
 | ||||
| func (k *String) Of(v string) label.Label { return label.OfString(k, v) } | ||||
| 
 | ||||
| // Get can be used to get a label for the key from a label.Map.
 | ||||
| func (k *String) Get(lm label.Map) string { | ||||
| 	if t := lm.Find(k); t.Valid() { | ||||
| 		return k.From(t) | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
| 
 | ||||
| // From can be used to get a value from a Label.
 | ||||
| func (k *String) From(t label.Label) string { return t.UnpackString() } | ||||
| 
 | ||||
| // Boolean represents a key
 | ||||
| type Boolean struct { | ||||
| 	name        string | ||||
| 	description string | ||||
| } | ||||
| 
 | ||||
| // NewBoolean creates a new Key for bool values.
 | ||||
| func NewBoolean(name, description string) *Boolean { | ||||
| 	return &Boolean{name: name, description: description} | ||||
| } | ||||
| 
 | ||||
| func (k *Boolean) Name() string        { return k.name } | ||||
| func (k *Boolean) Description() string { return k.description } | ||||
| 
 | ||||
| func (k *Boolean) Format(w io.Writer, buf []byte, l label.Label) { | ||||
| 	w.Write(strconv.AppendBool(buf, k.From(l))) | ||||
| } | ||||
| 
 | ||||
| // Of creates a new Label with this key and the supplied value.
 | ||||
| func (k *Boolean) Of(v bool) label.Label { | ||||
| 	if v { | ||||
| 		return label.Of64(k, 1) | ||||
| 	} | ||||
| 	return label.Of64(k, 0) | ||||
| } | ||||
| 
 | ||||
| // Get can be used to get a label for the key from a label.Map.
 | ||||
| func (k *Boolean) Get(lm label.Map) bool { | ||||
| 	if t := lm.Find(k); t.Valid() { | ||||
| 		return k.From(t) | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| // From can be used to get a value from a Label.
 | ||||
| func (k *Boolean) From(t label.Label) bool { return t.Unpack64() > 0 } | ||||
| 
 | ||||
| // Error represents a key
 | ||||
| type Error struct { | ||||
| 	name        string | ||||
| 	description string | ||||
| } | ||||
| 
 | ||||
| // NewError creates a new Key for int64 values.
 | ||||
| func NewError(name, description string) *Error { | ||||
| 	return &Error{name: name, description: description} | ||||
| } | ||||
| 
 | ||||
| func (k *Error) Name() string        { return k.name } | ||||
| func (k *Error) Description() string { return k.description } | ||||
| 
 | ||||
| func (k *Error) Format(w io.Writer, buf []byte, l label.Label) { | ||||
| 	io.WriteString(w, k.From(l).Error()) | ||||
| } | ||||
| 
 | ||||
| // Of creates a new Label with this key and the supplied value.
 | ||||
| func (k *Error) Of(v error) label.Label { return label.OfValue(k, v) } | ||||
| 
 | ||||
| // Get can be used to get a label for the key from a label.Map.
 | ||||
| func (k *Error) Get(lm label.Map) error { | ||||
| 	if t := lm.Find(k); t.Valid() { | ||||
| 		return k.From(t) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // From can be used to get a value from a Label.
 | ||||
| func (k *Error) From(t label.Label) error { | ||||
| 	err, _ := t.UnpackValue().(error) | ||||
| 	return err | ||||
| } | ||||
|  | @ -0,0 +1,22 @@ | |||
| // Copyright 2020 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.
 | ||||
| 
 | ||||
| package keys | ||||
| 
 | ||||
| var ( | ||||
| 	// Msg is a key used to add message strings to label lists.
 | ||||
| 	Msg = NewString("message", "a readable message") | ||||
| 	// Label is a key used to indicate an event adds labels to the context.
 | ||||
| 	Label = NewTag("label", "a label context marker") | ||||
| 	// Start is used for things like traces that have a name.
 | ||||
| 	Start = NewString("start", "span start") | ||||
| 	// Metric is a key used to indicate an event records metrics.
 | ||||
| 	End = NewTag("end", "a span end marker") | ||||
| 	// Metric is a key used to indicate an event records metrics.
 | ||||
| 	Detach = NewTag("detach", "a span detach marker") | ||||
| 	// Err is a key used to add error values to label lists.
 | ||||
| 	Err = NewError("error", "an error that occurred") | ||||
| 	// Metric is a key used to indicate an event records metrics.
 | ||||
| 	Metric = NewTag("metric", "a metric event marker") | ||||
| ) | ||||
|  | @ -0,0 +1,215 @@ | |||
| // Copyright 2019 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.
 | ||||
| 
 | ||||
| package label | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"reflect" | ||||
| 	"unsafe" | ||||
| ) | ||||
| 
 | ||||
| // Key is used as the identity of a Label.
 | ||||
| // Keys are intended to be compared by pointer only, the name should be unique
 | ||||
| // for communicating with external systems, but it is not required or enforced.
 | ||||
| type Key interface { | ||||
| 	// Name returns the key name.
 | ||||
| 	Name() string | ||||
| 	// Description returns a string that can be used to describe the value.
 | ||||
| 	Description() string | ||||
| 
 | ||||
| 	// Format is used in formatting to append the value of the label to the
 | ||||
| 	// supplied buffer.
 | ||||
| 	// The formatter may use the supplied buf as a scratch area to avoid
 | ||||
| 	// allocations.
 | ||||
| 	Format(w io.Writer, buf []byte, l Label) | ||||
| } | ||||
| 
 | ||||
| // Label holds a key and value pair.
 | ||||
| // It is normally used when passing around lists of labels.
 | ||||
| type Label struct { | ||||
| 	key     Key | ||||
| 	packed  uint64 | ||||
| 	untyped interface{} | ||||
| } | ||||
| 
 | ||||
| // Map is the interface to a collection of Labels indexed by key.
 | ||||
| type Map interface { | ||||
| 	// Find returns the label that matches the supplied key.
 | ||||
| 	Find(key Key) Label | ||||
| } | ||||
| 
 | ||||
| // List is the interface to something that provides an iterable
 | ||||
| // list of labels.
 | ||||
| // Iteration should start from 0 and continue until Valid returns false.
 | ||||
| type List interface { | ||||
| 	// Valid returns true if the index is within range for the list.
 | ||||
| 	// It does not imply the label at that index will itself be valid.
 | ||||
| 	Valid(index int) bool | ||||
| 	// Label returns the label at the given index.
 | ||||
| 	Label(index int) Label | ||||
| } | ||||
| 
 | ||||
| // list implements LabelList for a list of Labels.
 | ||||
| type list struct { | ||||
| 	labels []Label | ||||
| } | ||||
| 
 | ||||
| // filter wraps a LabelList filtering out specific labels.
 | ||||
| type filter struct { | ||||
| 	keys       []Key | ||||
| 	underlying List | ||||
| } | ||||
| 
 | ||||
| // listMap implements LabelMap for a simple list of labels.
 | ||||
| type listMap struct { | ||||
| 	labels []Label | ||||
| } | ||||
| 
 | ||||
| // mapChain implements LabelMap for a list of underlying LabelMap.
 | ||||
| type mapChain struct { | ||||
| 	maps []Map | ||||
| } | ||||
| 
 | ||||
| // OfValue creates a new label from the key and value.
 | ||||
| // This method is for implementing new key types, label creation should
 | ||||
| // normally be done with the Of method of the key.
 | ||||
| func OfValue(k Key, value interface{}) Label { return Label{key: k, untyped: value} } | ||||
| 
 | ||||
| // UnpackValue assumes the label was built using LabelOfValue and returns the value
 | ||||
| // that was passed to that constructor.
 | ||||
| // This method is for implementing new key types, for type safety normal
 | ||||
| // access should be done with the From method of the key.
 | ||||
| func (t Label) UnpackValue() interface{} { return t.untyped } | ||||
| 
 | ||||
| // Of64 creates a new label from a key and a uint64. This is often
 | ||||
| // used for non uint64 values that can be packed into a uint64.
 | ||||
| // This method is for implementing new key types, label creation should
 | ||||
| // normally be done with the Of method of the key.
 | ||||
| func Of64(k Key, v uint64) Label { return Label{key: k, packed: v} } | ||||
| 
 | ||||
| // Unpack64 assumes the label was built using LabelOf64 and returns the value that
 | ||||
| // was passed to that constructor.
 | ||||
| // This method is for implementing new key types, for type safety normal
 | ||||
| // access should be done with the From method of the key.
 | ||||
| func (t Label) Unpack64() uint64 { return t.packed } | ||||
| 
 | ||||
| type stringptr unsafe.Pointer | ||||
| 
 | ||||
| // OfString creates a new label from a key and a string.
 | ||||
| // This method is for implementing new key types, label creation should
 | ||||
| // normally be done with the Of method of the key.
 | ||||
| func OfString(k Key, v string) Label { | ||||
| 	hdr := (*reflect.StringHeader)(unsafe.Pointer(&v)) | ||||
| 	return Label{ | ||||
| 		key:     k, | ||||
| 		packed:  uint64(hdr.Len), | ||||
| 		untyped: stringptr(hdr.Data), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // UnpackString assumes the label was built using LabelOfString and returns the
 | ||||
| // value that was passed to that constructor.
 | ||||
| // This method is for implementing new key types, for type safety normal
 | ||||
| // access should be done with the From method of the key.
 | ||||
| func (t Label) UnpackString() string { | ||||
| 	var v string | ||||
| 	hdr := (*reflect.StringHeader)(unsafe.Pointer(&v)) | ||||
| 	hdr.Data = uintptr(t.untyped.(stringptr)) | ||||
| 	hdr.Len = int(t.packed) | ||||
| 	return v | ||||
| } | ||||
| 
 | ||||
| // Valid returns true if the Label is a valid one (it has a key).
 | ||||
| func (t Label) Valid() bool { return t.key != nil } | ||||
| 
 | ||||
| // Key returns the key of this Label.
 | ||||
| func (t Label) Key() Key { return t.key } | ||||
| 
 | ||||
| // Format is used for debug printing of labels.
 | ||||
| func (t Label) Format(f fmt.State, r rune) { | ||||
| 	if !t.Valid() { | ||||
| 		io.WriteString(f, `nil`) | ||||
| 		return | ||||
| 	} | ||||
| 	io.WriteString(f, t.Key().Name()) | ||||
| 	io.WriteString(f, "=") | ||||
| 	var buf [128]byte | ||||
| 	t.Key().Format(f, buf[:0], t) | ||||
| } | ||||
| 
 | ||||
| func (l *list) Valid(index int) bool { | ||||
| 	return index >= 0 && index < len(l.labels) | ||||
| } | ||||
| 
 | ||||
| func (l *list) Label(index int) Label { | ||||
| 	return l.labels[index] | ||||
| } | ||||
| 
 | ||||
| func (f *filter) Valid(index int) bool { | ||||
| 	return f.underlying.Valid(index) | ||||
| } | ||||
| 
 | ||||
| func (f *filter) Label(index int) Label { | ||||
| 	l := f.underlying.Label(index) | ||||
| 	for _, f := range f.keys { | ||||
| 		if l.Key() == f { | ||||
| 			return Label{} | ||||
| 		} | ||||
| 	} | ||||
| 	return l | ||||
| } | ||||
| 
 | ||||
| func (lm listMap) Find(key Key) Label { | ||||
| 	for _, l := range lm.labels { | ||||
| 		if l.Key() == key { | ||||
| 			return l | ||||
| 		} | ||||
| 	} | ||||
| 	return Label{} | ||||
| } | ||||
| 
 | ||||
| func (c mapChain) Find(key Key) Label { | ||||
| 	for _, src := range c.maps { | ||||
| 		l := src.Find(key) | ||||
| 		if l.Valid() { | ||||
| 			return l | ||||
| 		} | ||||
| 	} | ||||
| 	return Label{} | ||||
| } | ||||
| 
 | ||||
| var emptyList = &list{} | ||||
| 
 | ||||
| func NewList(labels ...Label) List { | ||||
| 	if len(labels) == 0 { | ||||
| 		return emptyList | ||||
| 	} | ||||
| 	return &list{labels: labels} | ||||
| } | ||||
| 
 | ||||
| func Filter(l List, keys ...Key) List { | ||||
| 	if len(keys) == 0 { | ||||
| 		return l | ||||
| 	} | ||||
| 	return &filter{keys: keys, underlying: l} | ||||
| } | ||||
| 
 | ||||
| func NewMap(labels ...Label) Map { | ||||
| 	return listMap{labels: labels} | ||||
| } | ||||
| 
 | ||||
| func MergeMaps(srcs ...Map) Map { | ||||
| 	var nonNil []Map | ||||
| 	for _, src := range srcs { | ||||
| 		if src != nil { | ||||
| 			nonNil = append(nonNil, src) | ||||
| 		} | ||||
| 	} | ||||
| 	if len(nonNil) == 1 { | ||||
| 		return nonNil[0] | ||||
| 	} | ||||
| 	return mapChain{maps: nonNil} | ||||
| } | ||||
|  | @ -0,0 +1,283 @@ | |||
| // Copyright 2020 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.
 | ||||
| 
 | ||||
| // Package gocommand is a helper for calling the go command.
 | ||||
| package gocommand | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"regexp" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"time" | ||||
| 
 | ||||
| 	exec "golang.org/x/sys/execabs" | ||||
| 
 | ||||
| 	"golang.org/x/tools/internal/event" | ||||
| ) | ||||
| 
 | ||||
| // An Runner will run go command invocations and serialize
 | ||||
| // them if it sees a concurrency error.
 | ||||
| type Runner struct { | ||||
| 	// once guards the runner initialization.
 | ||||
| 	once sync.Once | ||||
| 
 | ||||
| 	// inFlight tracks available workers.
 | ||||
| 	inFlight chan struct{} | ||||
| 
 | ||||
| 	// serialized guards the ability to run a go command serially,
 | ||||
| 	// to avoid deadlocks when claiming workers.
 | ||||
| 	serialized chan struct{} | ||||
| } | ||||
| 
 | ||||
| const maxInFlight = 10 | ||||
| 
 | ||||
| func (runner *Runner) initialize() { | ||||
| 	runner.once.Do(func() { | ||||
| 		runner.inFlight = make(chan struct{}, maxInFlight) | ||||
| 		runner.serialized = make(chan struct{}, 1) | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| // 1.13: go: updates to go.mod needed, but contents have changed
 | ||||
| // 1.14: go: updating go.mod: existing contents have changed since last read
 | ||||
| var modConcurrencyError = regexp.MustCompile(`go:.*go.mod.*contents have changed`) | ||||
| 
 | ||||
| // Run is a convenience wrapper around RunRaw.
 | ||||
| // It returns only stdout and a "friendly" error.
 | ||||
| func (runner *Runner) Run(ctx context.Context, inv Invocation) (*bytes.Buffer, error) { | ||||
| 	stdout, _, friendly, _ := runner.RunRaw(ctx, inv) | ||||
| 	return stdout, friendly | ||||
| } | ||||
| 
 | ||||
| // RunPiped runs the invocation serially, always waiting for any concurrent
 | ||||
| // invocations to complete first.
 | ||||
| func (runner *Runner) RunPiped(ctx context.Context, inv Invocation, stdout, stderr io.Writer) error { | ||||
| 	_, err := runner.runPiped(ctx, inv, stdout, stderr) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| // RunRaw runs the invocation, serializing requests only if they fight over
 | ||||
| // go.mod changes.
 | ||||
| func (runner *Runner) RunRaw(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) { | ||||
| 	// Make sure the runner is always initialized.
 | ||||
| 	runner.initialize() | ||||
| 
 | ||||
| 	// First, try to run the go command concurrently.
 | ||||
| 	stdout, stderr, friendlyErr, err := runner.runConcurrent(ctx, inv) | ||||
| 
 | ||||
| 	// If we encounter a load concurrency error, we need to retry serially.
 | ||||
| 	if friendlyErr == nil || !modConcurrencyError.MatchString(friendlyErr.Error()) { | ||||
| 		return stdout, stderr, friendlyErr, err | ||||
| 	} | ||||
| 	event.Error(ctx, "Load concurrency error, will retry serially", err) | ||||
| 
 | ||||
| 	// Run serially by calling runPiped.
 | ||||
| 	stdout.Reset() | ||||
| 	stderr.Reset() | ||||
| 	friendlyErr, err = runner.runPiped(ctx, inv, stdout, stderr) | ||||
| 	return stdout, stderr, friendlyErr, err | ||||
| } | ||||
| 
 | ||||
| func (runner *Runner) runConcurrent(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) { | ||||
| 	// Wait for 1 worker to become available.
 | ||||
| 	select { | ||||
| 	case <-ctx.Done(): | ||||
| 		return nil, nil, nil, ctx.Err() | ||||
| 	case runner.inFlight <- struct{}{}: | ||||
| 		defer func() { <-runner.inFlight }() | ||||
| 	} | ||||
| 
 | ||||
| 	stdout, stderr := &bytes.Buffer{}, &bytes.Buffer{} | ||||
| 	friendlyErr, err := inv.runWithFriendlyError(ctx, stdout, stderr) | ||||
| 	return stdout, stderr, friendlyErr, err | ||||
| } | ||||
| 
 | ||||
| func (runner *Runner) runPiped(ctx context.Context, inv Invocation, stdout, stderr io.Writer) (error, error) { | ||||
| 	// Make sure the runner is always initialized.
 | ||||
| 	runner.initialize() | ||||
| 
 | ||||
| 	// Acquire the serialization lock. This avoids deadlocks between two
 | ||||
| 	// runPiped commands.
 | ||||
| 	select { | ||||
| 	case <-ctx.Done(): | ||||
| 		return nil, ctx.Err() | ||||
| 	case runner.serialized <- struct{}{}: | ||||
| 		defer func() { <-runner.serialized }() | ||||
| 	} | ||||
| 
 | ||||
| 	// Wait for all in-progress go commands to return before proceeding,
 | ||||
| 	// to avoid load concurrency errors.
 | ||||
| 	for i := 0; i < maxInFlight; i++ { | ||||
| 		select { | ||||
| 		case <-ctx.Done(): | ||||
| 			return nil, ctx.Err() | ||||
| 		case runner.inFlight <- struct{}{}: | ||||
| 			// Make sure we always "return" any workers we took.
 | ||||
| 			defer func() { <-runner.inFlight }() | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return inv.runWithFriendlyError(ctx, stdout, stderr) | ||||
| } | ||||
| 
 | ||||
| // An Invocation represents a call to the go command.
 | ||||
| type Invocation struct { | ||||
| 	Verb       string | ||||
| 	Args       []string | ||||
| 	BuildFlags []string | ||||
| 
 | ||||
| 	// If ModFlag is set, the go command is invoked with -mod=ModFlag.
 | ||||
| 	ModFlag string | ||||
| 
 | ||||
| 	// If ModFile is set, the go command is invoked with -modfile=ModFile.
 | ||||
| 	ModFile string | ||||
| 
 | ||||
| 	// If Overlay is set, the go command is invoked with -overlay=Overlay.
 | ||||
| 	Overlay string | ||||
| 
 | ||||
| 	// If CleanEnv is set, the invocation will run only with the environment
 | ||||
| 	// in Env, not starting with os.Environ.
 | ||||
| 	CleanEnv   bool | ||||
| 	Env        []string | ||||
| 	WorkingDir string | ||||
| 	Logf       func(format string, args ...interface{}) | ||||
| } | ||||
| 
 | ||||
| func (i *Invocation) runWithFriendlyError(ctx context.Context, stdout, stderr io.Writer) (friendlyError error, rawError error) { | ||||
| 	rawError = i.run(ctx, stdout, stderr) | ||||
| 	if rawError != nil { | ||||
| 		friendlyError = rawError | ||||
| 		// Check for 'go' executable not being found.
 | ||||
| 		if ee, ok := rawError.(*exec.Error); ok && ee.Err == exec.ErrNotFound { | ||||
| 			friendlyError = fmt.Errorf("go command required, not found: %v", ee) | ||||
| 		} | ||||
| 		if ctx.Err() != nil { | ||||
| 			friendlyError = ctx.Err() | ||||
| 		} | ||||
| 		friendlyError = fmt.Errorf("err: %v: stderr: %s", friendlyError, stderr) | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (i *Invocation) run(ctx context.Context, stdout, stderr io.Writer) error { | ||||
| 	log := i.Logf | ||||
| 	if log == nil { | ||||
| 		log = func(string, ...interface{}) {} | ||||
| 	} | ||||
| 
 | ||||
| 	goArgs := []string{i.Verb} | ||||
| 
 | ||||
| 	appendModFile := func() { | ||||
| 		if i.ModFile != "" { | ||||
| 			goArgs = append(goArgs, "-modfile="+i.ModFile) | ||||
| 		} | ||||
| 	} | ||||
| 	appendModFlag := func() { | ||||
| 		if i.ModFlag != "" { | ||||
| 			goArgs = append(goArgs, "-mod="+i.ModFlag) | ||||
| 		} | ||||
| 	} | ||||
| 	appendOverlayFlag := func() { | ||||
| 		if i.Overlay != "" { | ||||
| 			goArgs = append(goArgs, "-overlay="+i.Overlay) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	switch i.Verb { | ||||
| 	case "env", "version": | ||||
| 		goArgs = append(goArgs, i.Args...) | ||||
| 	case "mod": | ||||
| 		// mod needs the sub-verb before flags.
 | ||||
| 		goArgs = append(goArgs, i.Args[0]) | ||||
| 		appendModFile() | ||||
| 		goArgs = append(goArgs, i.Args[1:]...) | ||||
| 	case "get": | ||||
| 		goArgs = append(goArgs, i.BuildFlags...) | ||||
| 		appendModFile() | ||||
| 		goArgs = append(goArgs, i.Args...) | ||||
| 
 | ||||
| 	default: // notably list and build.
 | ||||
| 		goArgs = append(goArgs, i.BuildFlags...) | ||||
| 		appendModFile() | ||||
| 		appendModFlag() | ||||
| 		appendOverlayFlag() | ||||
| 		goArgs = append(goArgs, i.Args...) | ||||
| 	} | ||||
| 	cmd := exec.Command("go", goArgs...) | ||||
| 	cmd.Stdout = stdout | ||||
| 	cmd.Stderr = stderr | ||||
| 	// On darwin the cwd gets resolved to the real path, which breaks anything that
 | ||||
| 	// expects the working directory to keep the original path, including the
 | ||||
| 	// go command when dealing with modules.
 | ||||
| 	// The Go stdlib has a special feature where if the cwd and the PWD are the
 | ||||
| 	// same node then it trusts the PWD, so by setting it in the env for the child
 | ||||
| 	// process we fix up all the paths returned by the go command.
 | ||||
| 	if !i.CleanEnv { | ||||
| 		cmd.Env = os.Environ() | ||||
| 	} | ||||
| 	cmd.Env = append(cmd.Env, i.Env...) | ||||
| 	if i.WorkingDir != "" { | ||||
| 		cmd.Env = append(cmd.Env, "PWD="+i.WorkingDir) | ||||
| 		cmd.Dir = i.WorkingDir | ||||
| 	} | ||||
| 	defer func(start time.Time) { log("%s for %v", time.Since(start), cmdDebugStr(cmd)) }(time.Now()) | ||||
| 
 | ||||
| 	return runCmdContext(ctx, cmd) | ||||
| } | ||||
| 
 | ||||
| // runCmdContext is like exec.CommandContext except it sends os.Interrupt
 | ||||
| // before os.Kill.
 | ||||
| func runCmdContext(ctx context.Context, cmd *exec.Cmd) error { | ||||
| 	if err := cmd.Start(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	resChan := make(chan error, 1) | ||||
| 	go func() { | ||||
| 		resChan <- cmd.Wait() | ||||
| 	}() | ||||
| 
 | ||||
| 	select { | ||||
| 	case err := <-resChan: | ||||
| 		return err | ||||
| 	case <-ctx.Done(): | ||||
| 	} | ||||
| 	// Cancelled. Interrupt and see if it ends voluntarily.
 | ||||
| 	cmd.Process.Signal(os.Interrupt) | ||||
| 	select { | ||||
| 	case err := <-resChan: | ||||
| 		return err | ||||
| 	case <-time.After(time.Second): | ||||
| 	} | ||||
| 	// Didn't shut down in response to interrupt. Kill it hard.
 | ||||
| 	cmd.Process.Kill() | ||||
| 	return <-resChan | ||||
| } | ||||
| 
 | ||||
| func cmdDebugStr(cmd *exec.Cmd) string { | ||||
| 	env := make(map[string]string) | ||||
| 	for _, kv := range cmd.Env { | ||||
| 		split := strings.SplitN(kv, "=", 2) | ||||
| 		if len(split) == 2 { | ||||
| 			k, v := split[0], split[1] | ||||
| 			env[k] = v | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	var args []string | ||||
| 	for _, arg := range cmd.Args { | ||||
| 		quoted := strconv.Quote(arg) | ||||
| 		if quoted[1:len(quoted)-1] != arg || strings.Contains(arg, " ") { | ||||
| 			args = append(args, quoted) | ||||
| 		} else { | ||||
| 			args = append(args, arg) | ||||
| 		} | ||||
| 	} | ||||
| 	return fmt.Sprintf("GOROOT=%v GOPATH=%v GO111MODULE=%v GOPROXY=%v PWD=%v %v", env["GOROOT"], env["GOPATH"], env["GO111MODULE"], env["GOPROXY"], env["PWD"], strings.Join(args, " ")) | ||||
| } | ||||
|  | @ -0,0 +1,109 @@ | |||
| // Copyright 2020 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.
 | ||||
| 
 | ||||
| package gocommand | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"regexp" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"golang.org/x/mod/semver" | ||||
| ) | ||||
| 
 | ||||
| // ModuleJSON holds information about a module.
 | ||||
| type ModuleJSON struct { | ||||
| 	Path      string      // module path
 | ||||
| 	Version   string      // module version
 | ||||
| 	Versions  []string    // available module versions (with -versions)
 | ||||
| 	Replace   *ModuleJSON // replaced by this module
 | ||||
| 	Time      *time.Time  // time version was created
 | ||||
| 	Update    *ModuleJSON // available update, if any (with -u)
 | ||||
| 	Main      bool        // is this the main module?
 | ||||
| 	Indirect  bool        // is this module only an indirect dependency of main module?
 | ||||
| 	Dir       string      // directory holding files for this module, if any
 | ||||
| 	GoMod     string      // path to go.mod file used when loading this module, if any
 | ||||
| 	GoVersion string      // go version used in module
 | ||||
| } | ||||
| 
 | ||||
| var modFlagRegexp = regexp.MustCompile(`-mod[ =](\w+)`) | ||||
| 
 | ||||
| // VendorEnabled reports whether vendoring is enabled. It takes a *Runner to execute Go commands
 | ||||
| // with the supplied context.Context and Invocation. The Invocation can contain pre-defined fields,
 | ||||
| // of which only Verb and Args are modified to run the appropriate Go command.
 | ||||
| // Inspired by setDefaultBuildMod in modload/init.go
 | ||||
| func VendorEnabled(ctx context.Context, inv Invocation, r *Runner) (bool, *ModuleJSON, error) { | ||||
| 	mainMod, go114, err := getMainModuleAnd114(ctx, inv, r) | ||||
| 	if err != nil { | ||||
| 		return false, nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	// We check the GOFLAGS to see if there is anything overridden or not.
 | ||||
| 	inv.Verb = "env" | ||||
| 	inv.Args = []string{"GOFLAGS"} | ||||
| 	stdout, err := r.Run(ctx, inv) | ||||
| 	if err != nil { | ||||
| 		return false, nil, err | ||||
| 	} | ||||
| 	goflags := string(bytes.TrimSpace(stdout.Bytes())) | ||||
| 	matches := modFlagRegexp.FindStringSubmatch(goflags) | ||||
| 	var modFlag string | ||||
| 	if len(matches) != 0 { | ||||
| 		modFlag = matches[1] | ||||
| 	} | ||||
| 	// Don't override an explicit '-mod=' argument.
 | ||||
| 	if modFlag == "vendor" { | ||||
| 		return true, mainMod, nil | ||||
| 	} else if modFlag != "" { | ||||
| 		return false, nil, nil | ||||
| 	} | ||||
| 	if mainMod == nil || !go114 { | ||||
| 		return false, nil, nil | ||||
| 	} | ||||
| 	// Check 1.14's automatic vendor mode.
 | ||||
| 	if fi, err := os.Stat(filepath.Join(mainMod.Dir, "vendor")); err == nil && fi.IsDir() { | ||||
| 		if mainMod.GoVersion != "" && semver.Compare("v"+mainMod.GoVersion, "v1.14") >= 0 { | ||||
| 			// The Go version is at least 1.14, and a vendor directory exists.
 | ||||
| 			// Set -mod=vendor by default.
 | ||||
| 			return true, mainMod, nil | ||||
| 		} | ||||
| 	} | ||||
| 	return false, nil, nil | ||||
| } | ||||
| 
 | ||||
| // getMainModuleAnd114 gets one of the main modules' information and whether the
 | ||||
| // go command in use is 1.14+. This is the information needed to figure out
 | ||||
| // if vendoring should be enabled.
 | ||||
| func getMainModuleAnd114(ctx context.Context, inv Invocation, r *Runner) (*ModuleJSON, bool, error) { | ||||
| 	const format = `{{.Path}} | ||||
| {{.Dir}} | ||||
| {{.GoMod}} | ||||
| {{.GoVersion}} | ||||
| {{range context.ReleaseTags}}{{if eq . "go1.14"}}{{.}}{{end}}{{end}} | ||||
| ` | ||||
| 	inv.Verb = "list" | ||||
| 	inv.Args = []string{"-m", "-f", format} | ||||
| 	stdout, err := r.Run(ctx, inv) | ||||
| 	if err != nil { | ||||
| 		return nil, false, err | ||||
| 	} | ||||
| 
 | ||||
| 	lines := strings.Split(stdout.String(), "\n") | ||||
| 	if len(lines) < 5 { | ||||
| 		return nil, false, fmt.Errorf("unexpected stdout: %q", stdout.String()) | ||||
| 	} | ||||
| 	mod := &ModuleJSON{ | ||||
| 		Path:      lines[0], | ||||
| 		Dir:       lines[1], | ||||
| 		GoMod:     lines[2], | ||||
| 		GoVersion: lines[3], | ||||
| 		Main:      true, | ||||
| 	} | ||||
| 	return mod, lines[4] == "go1.14", nil | ||||
| } | ||||
|  | @ -0,0 +1,51 @@ | |||
| // Copyright 2020 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.
 | ||||
| 
 | ||||
| package gocommand | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // GoVersion checks the go version by running "go list" with modules off.
 | ||||
| // It returns the X in Go 1.X.
 | ||||
| func GoVersion(ctx context.Context, inv Invocation, r *Runner) (int, error) { | ||||
| 	inv.Verb = "list" | ||||
| 	inv.Args = []string{"-e", "-f", `{{context.ReleaseTags}}`, `--`, `unsafe`} | ||||
| 	inv.Env = append(append([]string{}, inv.Env...), "GO111MODULE=off") | ||||
| 	// Unset any unneeded flags, and remove them from BuildFlags, if they're
 | ||||
| 	// present.
 | ||||
| 	inv.ModFile = "" | ||||
| 	inv.ModFlag = "" | ||||
| 	var buildFlags []string | ||||
| 	for _, flag := range inv.BuildFlags { | ||||
| 		// Flags can be prefixed by one or two dashes.
 | ||||
| 		f := strings.TrimPrefix(strings.TrimPrefix(flag, "-"), "-") | ||||
| 		if strings.HasPrefix(f, "mod=") || strings.HasPrefix(f, "modfile=") { | ||||
| 			continue | ||||
| 		} | ||||
| 		buildFlags = append(buildFlags, flag) | ||||
| 	} | ||||
| 	inv.BuildFlags = buildFlags | ||||
| 	stdoutBytes, err := r.Run(ctx, inv) | ||||
| 	if err != nil { | ||||
| 		return 0, err | ||||
| 	} | ||||
| 	stdout := stdoutBytes.String() | ||||
| 	if len(stdout) < 3 { | ||||
| 		return 0, fmt.Errorf("bad ReleaseTags output: %q", stdout) | ||||
| 	} | ||||
| 	// Split up "[go1.1 go1.15]"
 | ||||
| 	tags := strings.Fields(stdout[1 : len(stdout)-2]) | ||||
| 	for i := len(tags) - 1; i >= 0; i-- { | ||||
| 		var version int | ||||
| 		if _, err := fmt.Sscanf(tags[i], "go1.%d", &version); err != nil { | ||||
| 			continue | ||||
| 		} | ||||
| 		return version, nil | ||||
| 	} | ||||
| 	return 0, fmt.Errorf("no parseable ReleaseTags in %v", tags) | ||||
| } | ||||
|  | @ -0,0 +1,30 @@ | |||
| // Copyright 2020 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.
 | ||||
| 
 | ||||
| // Package packagesinternal exposes internal-only fields from go/packages.
 | ||||
| package packagesinternal | ||||
| 
 | ||||
| import ( | ||||
| 	"golang.org/x/tools/internal/gocommand" | ||||
| ) | ||||
| 
 | ||||
| var GetForTest = func(p interface{}) string { return "" } | ||||
| var GetDepsErrors = func(p interface{}) []*PackageError { return nil } | ||||
| 
 | ||||
| type PackageError struct { | ||||
| 	ImportStack []string // shortest path from package named on command line to this one
 | ||||
| 	Pos         string   // position of error (if present, file:line:col)
 | ||||
| 	Err         string   // the error itself
 | ||||
| } | ||||
| 
 | ||||
| var GetGoCmdRunner = func(config interface{}) *gocommand.Runner { return nil } | ||||
| 
 | ||||
| var SetGoCmdRunner = func(config interface{}, runner *gocommand.Runner) {} | ||||
| 
 | ||||
| var TypecheckCgo int | ||||
| var DepsErrors int // must be set as a LoadMode to call GetDepsErrors
 | ||||
| var ForTest int    // must be set as a LoadMode to call GetForTest
 | ||||
| 
 | ||||
| var SetModFlag = func(config interface{}, value string) {} | ||||
| var SetModFile = func(config interface{}, value string) {} | ||||
|  | @ -0,0 +1,179 @@ | |||
| // Copyright 2021 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.
 | ||||
| 
 | ||||
| // Package typeparams contains common utilities for writing tools that interact
 | ||||
| // with generic Go code, as introduced with Go 1.18.
 | ||||
| //
 | ||||
| // Many of the types and functions in this package are proxies for the new APIs
 | ||||
| // introduced in the standard library with Go 1.18. For example, the
 | ||||
| // typeparams.Union type is an alias for go/types.Union, and the ForTypeSpec
 | ||||
| // function returns the value of the go/ast.TypeSpec.TypeParams field. At Go
 | ||||
| // versions older than 1.18 these helpers are implemented as stubs, allowing
 | ||||
| // users of this package to write code that handles generic constructs inline,
 | ||||
| // even if the Go version being used to compile does not support generics.
 | ||||
| //
 | ||||
| // Additionally, this package contains common utilities for working with the
 | ||||
| // new generic constructs, to supplement the standard library APIs. Notably,
 | ||||
| // the StructuralTerms API computes a minimal representation of the structural
 | ||||
| // restrictions on a type parameter.
 | ||||
| //
 | ||||
| // An external version of these APIs is available in the
 | ||||
| // golang.org/x/exp/typeparams module.
 | ||||
| package typeparams | ||||
| 
 | ||||
| import ( | ||||
| 	"go/ast" | ||||
| 	"go/token" | ||||
| 	"go/types" | ||||
| ) | ||||
| 
 | ||||
| // UnpackIndexExpr extracts data from AST nodes that represent index
 | ||||
| // expressions.
 | ||||
| //
 | ||||
| // For an ast.IndexExpr, the resulting indices slice will contain exactly one
 | ||||
| // index expression. For an ast.IndexListExpr (go1.18+), it may have a variable
 | ||||
| // number of index expressions.
 | ||||
| //
 | ||||
| // For nodes that don't represent index expressions, the first return value of
 | ||||
| // UnpackIndexExpr will be nil.
 | ||||
| func UnpackIndexExpr(n ast.Node) (x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack token.Pos) { | ||||
| 	switch e := n.(type) { | ||||
| 	case *ast.IndexExpr: | ||||
| 		return e.X, e.Lbrack, []ast.Expr{e.Index}, e.Rbrack | ||||
| 	case *IndexListExpr: | ||||
| 		return e.X, e.Lbrack, e.Indices, e.Rbrack | ||||
| 	} | ||||
| 	return nil, token.NoPos, nil, token.NoPos | ||||
| } | ||||
| 
 | ||||
| // PackIndexExpr returns an *ast.IndexExpr or *ast.IndexListExpr, depending on
 | ||||
| // the cardinality of indices. Calling PackIndexExpr with len(indices) == 0
 | ||||
| // will panic.
 | ||||
| func PackIndexExpr(x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack token.Pos) ast.Expr { | ||||
| 	switch len(indices) { | ||||
| 	case 0: | ||||
| 		panic("empty indices") | ||||
| 	case 1: | ||||
| 		return &ast.IndexExpr{ | ||||
| 			X:      x, | ||||
| 			Lbrack: lbrack, | ||||
| 			Index:  indices[0], | ||||
| 			Rbrack: rbrack, | ||||
| 		} | ||||
| 	default: | ||||
| 		return &IndexListExpr{ | ||||
| 			X:       x, | ||||
| 			Lbrack:  lbrack, | ||||
| 			Indices: indices, | ||||
| 			Rbrack:  rbrack, | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // IsTypeParam reports whether t is a type parameter.
 | ||||
| func IsTypeParam(t types.Type) bool { | ||||
| 	_, ok := t.(*TypeParam) | ||||
| 	return ok | ||||
| } | ||||
| 
 | ||||
| // OriginMethod returns the origin method associated with the method fn.
 | ||||
| // For methods on a non-generic receiver base type, this is just
 | ||||
| // fn. However, for methods with a generic receiver, OriginMethod returns the
 | ||||
| // corresponding method in the method set of the origin type.
 | ||||
| //
 | ||||
| // As a special case, if fn is not a method (has no receiver), OriginMethod
 | ||||
| // returns fn.
 | ||||
| func OriginMethod(fn *types.Func) *types.Func { | ||||
| 	recv := fn.Type().(*types.Signature).Recv() | ||||
| 	if recv == nil { | ||||
| 
 | ||||
| 		return fn | ||||
| 	} | ||||
| 	base := recv.Type() | ||||
| 	p, isPtr := base.(*types.Pointer) | ||||
| 	if isPtr { | ||||
| 		base = p.Elem() | ||||
| 	} | ||||
| 	named, isNamed := base.(*types.Named) | ||||
| 	if !isNamed { | ||||
| 		// Receiver is a *types.Interface.
 | ||||
| 		return fn | ||||
| 	} | ||||
| 	if ForNamed(named).Len() == 0 { | ||||
| 		// Receiver base has no type parameters, so we can avoid the lookup below.
 | ||||
| 		return fn | ||||
| 	} | ||||
| 	orig := NamedTypeOrigin(named) | ||||
| 	gfn, _, _ := types.LookupFieldOrMethod(orig, true, fn.Pkg(), fn.Name()) | ||||
| 	return gfn.(*types.Func) | ||||
| } | ||||
| 
 | ||||
| // GenericAssignableTo is a generalization of types.AssignableTo that
 | ||||
| // implements the following rule for uninstantiated generic types:
 | ||||
| //
 | ||||
| // If V and T are generic named types, then V is considered assignable to T if,
 | ||||
| // for every possible instantation of V[A_1, ..., A_N], the instantiation
 | ||||
| // T[A_1, ..., A_N] is valid and V[A_1, ..., A_N] implements T[A_1, ..., A_N].
 | ||||
| //
 | ||||
| // If T has structural constraints, they must be satisfied by V.
 | ||||
| //
 | ||||
| // For example, consider the following type declarations:
 | ||||
| //
 | ||||
| //	type Interface[T any] interface {
 | ||||
| //		Accept(T)
 | ||||
| //	}
 | ||||
| //
 | ||||
| //	type Container[T any] struct {
 | ||||
| //		Element T
 | ||||
| //	}
 | ||||
| //
 | ||||
| //	func (c Container[T]) Accept(t T) { c.Element = t }
 | ||||
| //
 | ||||
| // In this case, GenericAssignableTo reports that instantiations of Container
 | ||||
| // are assignable to the corresponding instantiation of Interface.
 | ||||
| func GenericAssignableTo(ctxt *Context, V, T types.Type) bool { | ||||
| 	// If V and T are not both named, or do not have matching non-empty type
 | ||||
| 	// parameter lists, fall back on types.AssignableTo.
 | ||||
| 
 | ||||
| 	VN, Vnamed := V.(*types.Named) | ||||
| 	TN, Tnamed := T.(*types.Named) | ||||
| 	if !Vnamed || !Tnamed { | ||||
| 		return types.AssignableTo(V, T) | ||||
| 	} | ||||
| 
 | ||||
| 	vtparams := ForNamed(VN) | ||||
| 	ttparams := ForNamed(TN) | ||||
| 	if vtparams.Len() == 0 || vtparams.Len() != ttparams.Len() || NamedTypeArgs(VN).Len() != 0 || NamedTypeArgs(TN).Len() != 0 { | ||||
| 		return types.AssignableTo(V, T) | ||||
| 	} | ||||
| 
 | ||||
| 	// V and T have the same (non-zero) number of type params. Instantiate both
 | ||||
| 	// with the type parameters of V. This must always succeed for V, and will
 | ||||
| 	// succeed for T if and only if the type set of each type parameter of V is a
 | ||||
| 	// subset of the type set of the corresponding type parameter of T, meaning
 | ||||
| 	// that every instantiation of V corresponds to a valid instantiation of T.
 | ||||
| 
 | ||||
| 	// Minor optimization: ensure we share a context across the two
 | ||||
| 	// instantiations below.
 | ||||
| 	if ctxt == nil { | ||||
| 		ctxt = NewContext() | ||||
| 	} | ||||
| 
 | ||||
| 	var targs []types.Type | ||||
| 	for i := 0; i < vtparams.Len(); i++ { | ||||
| 		targs = append(targs, vtparams.At(i)) | ||||
| 	} | ||||
| 
 | ||||
| 	vinst, err := Instantiate(ctxt, V, targs, true) | ||||
| 	if err != nil { | ||||
| 		panic("type parameters should satisfy their own constraints") | ||||
| 	} | ||||
| 
 | ||||
| 	tinst, err := Instantiate(ctxt, T, targs, true) | ||||
| 	if err != nil { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	return types.AssignableTo(vinst, tinst) | ||||
| } | ||||
|  | @ -0,0 +1,122 @@ | |||
| // Copyright 2022 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.
 | ||||
| 
 | ||||
| package typeparams | ||||
| 
 | ||||
| import ( | ||||
| 	"go/types" | ||||
| ) | ||||
| 
 | ||||
| // CoreType returns the core type of T or nil if T does not have a core type.
 | ||||
| //
 | ||||
| // See https://go.dev/ref/spec#Core_types for the definition of a core type.
 | ||||
| func CoreType(T types.Type) types.Type { | ||||
| 	U := T.Underlying() | ||||
| 	if _, ok := U.(*types.Interface); !ok { | ||||
| 		return U // for non-interface types,
 | ||||
| 	} | ||||
| 
 | ||||
| 	terms, err := _NormalTerms(U) | ||||
| 	if len(terms) == 0 || err != nil { | ||||
| 		// len(terms) -> empty type set of interface.
 | ||||
| 		// err != nil => U is invalid, exceeds complexity bounds, or has an empty type set.
 | ||||
| 		return nil // no core type.
 | ||||
| 	} | ||||
| 
 | ||||
| 	U = terms[0].Type().Underlying() | ||||
| 	var identical int // i in [0,identical) => Identical(U, terms[i].Type().Underlying())
 | ||||
| 	for identical = 1; identical < len(terms); identical++ { | ||||
| 		if !types.Identical(U, terms[identical].Type().Underlying()) { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if identical == len(terms) { | ||||
| 		// https://go.dev/ref/spec#Core_types
 | ||||
| 		// "There is a single type U which is the underlying type of all types in the type set of T"
 | ||||
| 		return U | ||||
| 	} | ||||
| 	ch, ok := U.(*types.Chan) | ||||
| 	if !ok { | ||||
| 		return nil // no core type as identical < len(terms) and U is not a channel.
 | ||||
| 	} | ||||
| 	// https://go.dev/ref/spec#Core_types
 | ||||
| 	// "the type chan E if T contains only bidirectional channels, or the type chan<- E or
 | ||||
| 	// <-chan E depending on the direction of the directional channels present."
 | ||||
| 	for chans := identical; chans < len(terms); chans++ { | ||||
| 		curr, ok := terms[chans].Type().Underlying().(*types.Chan) | ||||
| 		if !ok { | ||||
| 			return nil | ||||
| 		} | ||||
| 		if !types.Identical(ch.Elem(), curr.Elem()) { | ||||
| 			return nil // channel elements are not identical.
 | ||||
| 		} | ||||
| 		if ch.Dir() == types.SendRecv { | ||||
| 			// ch is bidirectional. We can safely always use curr's direction.
 | ||||
| 			ch = curr | ||||
| 		} else if curr.Dir() != types.SendRecv && ch.Dir() != curr.Dir() { | ||||
| 			// ch and curr are not bidirectional and not the same direction.
 | ||||
| 			return nil | ||||
| 		} | ||||
| 	} | ||||
| 	return ch | ||||
| } | ||||
| 
 | ||||
| // _NormalTerms returns a slice of terms representing the normalized structural
 | ||||
| // type restrictions of a type, if any.
 | ||||
| //
 | ||||
| // For all types other than *types.TypeParam, *types.Interface, and
 | ||||
| // *types.Union, this is just a single term with Tilde() == false and
 | ||||
| // Type() == typ. For *types.TypeParam, *types.Interface, and *types.Union, see
 | ||||
| // below.
 | ||||
| //
 | ||||
| // Structural type restrictions of a type parameter are created via
 | ||||
| // non-interface types embedded in its constraint interface (directly, or via a
 | ||||
| // chain of interface embeddings). For example, in the declaration type
 | ||||
| // T[P interface{~int; m()}] int the structural restriction of the type
 | ||||
| // parameter P is ~int.
 | ||||
| //
 | ||||
| // With interface embedding and unions, the specification of structural type
 | ||||
| // restrictions may be arbitrarily complex. For example, consider the
 | ||||
| // following:
 | ||||
| //
 | ||||
| //  type A interface{ ~string|~[]byte }
 | ||||
| //
 | ||||
| //  type B interface{ int|string }
 | ||||
| //
 | ||||
| //  type C interface { ~string|~int }
 | ||||
| //
 | ||||
| //  type T[P interface{ A|B; C }] int
 | ||||
| //
 | ||||
| // In this example, the structural type restriction of P is ~string|int: A|B
 | ||||
| // expands to ~string|~[]byte|int|string, which reduces to ~string|~[]byte|int,
 | ||||
| // which when intersected with C (~string|~int) yields ~string|int.
 | ||||
| //
 | ||||
| // _NormalTerms computes these expansions and reductions, producing a
 | ||||
| // "normalized" form of the embeddings. A structural restriction is normalized
 | ||||
| // if it is a single union containing no interface terms, and is minimal in the
 | ||||
| // sense that removing any term changes the set of types satisfying the
 | ||||
| // constraint. It is left as a proof for the reader that, modulo sorting, there
 | ||||
| // is exactly one such normalized form.
 | ||||
| //
 | ||||
| // Because the minimal representation always takes this form, _NormalTerms
 | ||||
| // returns a slice of tilde terms corresponding to the terms of the union in
 | ||||
| // the normalized structural restriction. An error is returned if the type is
 | ||||
| // invalid, exceeds complexity bounds, or has an empty type set. In the latter
 | ||||
| // case, _NormalTerms returns ErrEmptyTypeSet.
 | ||||
| //
 | ||||
| // _NormalTerms makes no guarantees about the order of terms, except that it
 | ||||
| // is deterministic.
 | ||||
| func _NormalTerms(typ types.Type) ([]*Term, error) { | ||||
| 	switch typ := typ.(type) { | ||||
| 	case *TypeParam: | ||||
| 		return StructuralTerms(typ) | ||||
| 	case *Union: | ||||
| 		return UnionTermSet(typ) | ||||
| 	case *types.Interface: | ||||
| 		return InterfaceTermSet(typ) | ||||
| 	default: | ||||
| 		return []*Term{NewTerm(false, typ)}, nil | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,12 @@ | |||
| // Copyright 2021 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.
 | ||||
| 
 | ||||
| //go:build !go1.18
 | ||||
| // +build !go1.18
 | ||||
| 
 | ||||
| package typeparams | ||||
| 
 | ||||
| // Enabled reports whether type parameters are enabled in the current build
 | ||||
| // environment.
 | ||||
| const Enabled = false | ||||
|  | @ -0,0 +1,15 @@ | |||
| // Copyright 2021 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.
 | ||||
| 
 | ||||
| //go:build go1.18
 | ||||
| // +build go1.18
 | ||||
| 
 | ||||
| package typeparams | ||||
| 
 | ||||
| // Note: this constant is in a separate file as this is the only acceptable
 | ||||
| // diff between the <1.18 API of this package and the 1.18 API.
 | ||||
| 
 | ||||
| // Enabled reports whether type parameters are enabled in the current build
 | ||||
| // environment.
 | ||||
| const Enabled = true | ||||
|  | @ -0,0 +1,218 @@ | |||
| // Copyright 2021 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.
 | ||||
| 
 | ||||
| package typeparams | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"go/types" | ||||
| 	"os" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| //go:generate go run copytermlist.go
 | ||||
| 
 | ||||
| const debug = false | ||||
| 
 | ||||
| var ErrEmptyTypeSet = errors.New("empty type set") | ||||
| 
 | ||||
| // StructuralTerms returns a slice of terms representing the normalized
 | ||||
| // structural type restrictions of a type parameter, if any.
 | ||||
| //
 | ||||
| // Structural type restrictions of a type parameter are created via
 | ||||
| // non-interface types embedded in its constraint interface (directly, or via a
 | ||||
| // chain of interface embeddings). For example, in the declaration
 | ||||
| //
 | ||||
| //	type T[P interface{~int; m()}] int
 | ||||
| //
 | ||||
| // the structural restriction of the type parameter P is ~int.
 | ||||
| //
 | ||||
| // With interface embedding and unions, the specification of structural type
 | ||||
| // restrictions may be arbitrarily complex. For example, consider the
 | ||||
| // following:
 | ||||
| //
 | ||||
| //	type A interface{ ~string|~[]byte }
 | ||||
| //
 | ||||
| //	type B interface{ int|string }
 | ||||
| //
 | ||||
| //	type C interface { ~string|~int }
 | ||||
| //
 | ||||
| //	type T[P interface{ A|B; C }] int
 | ||||
| //
 | ||||
| // In this example, the structural type restriction of P is ~string|int: A|B
 | ||||
| // expands to ~string|~[]byte|int|string, which reduces to ~string|~[]byte|int,
 | ||||
| // which when intersected with C (~string|~int) yields ~string|int.
 | ||||
| //
 | ||||
| // StructuralTerms computes these expansions and reductions, producing a
 | ||||
| // "normalized" form of the embeddings. A structural restriction is normalized
 | ||||
| // if it is a single union containing no interface terms, and is minimal in the
 | ||||
| // sense that removing any term changes the set of types satisfying the
 | ||||
| // constraint. It is left as a proof for the reader that, modulo sorting, there
 | ||||
| // is exactly one such normalized form.
 | ||||
| //
 | ||||
| // Because the minimal representation always takes this form, StructuralTerms
 | ||||
| // returns a slice of tilde terms corresponding to the terms of the union in
 | ||||
| // the normalized structural restriction. An error is returned if the
 | ||||
| // constraint interface is invalid, exceeds complexity bounds, or has an empty
 | ||||
| // type set. In the latter case, StructuralTerms returns ErrEmptyTypeSet.
 | ||||
| //
 | ||||
| // StructuralTerms makes no guarantees about the order of terms, except that it
 | ||||
| // is deterministic.
 | ||||
| func StructuralTerms(tparam *TypeParam) ([]*Term, error) { | ||||
| 	constraint := tparam.Constraint() | ||||
| 	if constraint == nil { | ||||
| 		return nil, fmt.Errorf("%s has nil constraint", tparam) | ||||
| 	} | ||||
| 	iface, _ := constraint.Underlying().(*types.Interface) | ||||
| 	if iface == nil { | ||||
| 		return nil, fmt.Errorf("constraint is %T, not *types.Interface", constraint.Underlying()) | ||||
| 	} | ||||
| 	return InterfaceTermSet(iface) | ||||
| } | ||||
| 
 | ||||
| // InterfaceTermSet computes the normalized terms for a constraint interface,
 | ||||
| // returning an error if the term set cannot be computed or is empty. In the
 | ||||
| // latter case, the error will be ErrEmptyTypeSet.
 | ||||
| //
 | ||||
| // See the documentation of StructuralTerms for more information on
 | ||||
| // normalization.
 | ||||
| func InterfaceTermSet(iface *types.Interface) ([]*Term, error) { | ||||
| 	return computeTermSet(iface) | ||||
| } | ||||
| 
 | ||||
| // UnionTermSet computes the normalized terms for a union, returning an error
 | ||||
| // if the term set cannot be computed or is empty. In the latter case, the
 | ||||
| // error will be ErrEmptyTypeSet.
 | ||||
| //
 | ||||
| // See the documentation of StructuralTerms for more information on
 | ||||
| // normalization.
 | ||||
| func UnionTermSet(union *Union) ([]*Term, error) { | ||||
| 	return computeTermSet(union) | ||||
| } | ||||
| 
 | ||||
| func computeTermSet(typ types.Type) ([]*Term, error) { | ||||
| 	tset, err := computeTermSetInternal(typ, make(map[types.Type]*termSet), 0) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if tset.terms.isEmpty() { | ||||
| 		return nil, ErrEmptyTypeSet | ||||
| 	} | ||||
| 	if tset.terms.isAll() { | ||||
| 		return nil, nil | ||||
| 	} | ||||
| 	var terms []*Term | ||||
| 	for _, term := range tset.terms { | ||||
| 		terms = append(terms, NewTerm(term.tilde, term.typ)) | ||||
| 	} | ||||
| 	return terms, nil | ||||
| } | ||||
| 
 | ||||
| // A termSet holds the normalized set of terms for a given type.
 | ||||
| //
 | ||||
| // The name termSet is intentionally distinct from 'type set': a type set is
 | ||||
| // all types that implement a type (and includes method restrictions), whereas
 | ||||
| // a term set just represents the structural restrictions on a type.
 | ||||
| type termSet struct { | ||||
| 	complete bool | ||||
| 	terms    termlist | ||||
| } | ||||
| 
 | ||||
| func indentf(depth int, format string, args ...interface{}) { | ||||
| 	fmt.Fprintf(os.Stderr, strings.Repeat(".", depth)+format+"\n", args...) | ||||
| } | ||||
| 
 | ||||
| func computeTermSetInternal(t types.Type, seen map[types.Type]*termSet, depth int) (res *termSet, err error) { | ||||
| 	if t == nil { | ||||
| 		panic("nil type") | ||||
| 	} | ||||
| 
 | ||||
| 	if debug { | ||||
| 		indentf(depth, "%s", t.String()) | ||||
| 		defer func() { | ||||
| 			if err != nil { | ||||
| 				indentf(depth, "=> %s", err) | ||||
| 			} else { | ||||
| 				indentf(depth, "=> %s", res.terms.String()) | ||||
| 			} | ||||
| 		}() | ||||
| 	} | ||||
| 
 | ||||
| 	const maxTermCount = 100 | ||||
| 	if tset, ok := seen[t]; ok { | ||||
| 		if !tset.complete { | ||||
| 			return nil, fmt.Errorf("cycle detected in the declaration of %s", t) | ||||
| 		} | ||||
| 		return tset, nil | ||||
| 	} | ||||
| 
 | ||||
| 	// Mark the current type as seen to avoid infinite recursion.
 | ||||
| 	tset := new(termSet) | ||||
| 	defer func() { | ||||
| 		tset.complete = true | ||||
| 	}() | ||||
| 	seen[t] = tset | ||||
| 
 | ||||
| 	switch u := t.Underlying().(type) { | ||||
| 	case *types.Interface: | ||||
| 		// The term set of an interface is the intersection of the term sets of its
 | ||||
| 		// embedded types.
 | ||||
| 		tset.terms = allTermlist | ||||
| 		for i := 0; i < u.NumEmbeddeds(); i++ { | ||||
| 			embedded := u.EmbeddedType(i) | ||||
| 			if _, ok := embedded.Underlying().(*TypeParam); ok { | ||||
| 				return nil, fmt.Errorf("invalid embedded type %T", embedded) | ||||
| 			} | ||||
| 			tset2, err := computeTermSetInternal(embedded, seen, depth+1) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			tset.terms = tset.terms.intersect(tset2.terms) | ||||
| 		} | ||||
| 	case *Union: | ||||
| 		// The term set of a union is the union of term sets of its terms.
 | ||||
| 		tset.terms = nil | ||||
| 		for i := 0; i < u.Len(); i++ { | ||||
| 			t := u.Term(i) | ||||
| 			var terms termlist | ||||
| 			switch t.Type().Underlying().(type) { | ||||
| 			case *types.Interface: | ||||
| 				tset2, err := computeTermSetInternal(t.Type(), seen, depth+1) | ||||
| 				if err != nil { | ||||
| 					return nil, err | ||||
| 				} | ||||
| 				terms = tset2.terms | ||||
| 			case *TypeParam, *Union: | ||||
| 				// A stand-alone type parameter or union is not permitted as union
 | ||||
| 				// term.
 | ||||
| 				return nil, fmt.Errorf("invalid union term %T", t) | ||||
| 			default: | ||||
| 				if t.Type() == types.Typ[types.Invalid] { | ||||
| 					continue | ||||
| 				} | ||||
| 				terms = termlist{{t.Tilde(), t.Type()}} | ||||
| 			} | ||||
| 			tset.terms = tset.terms.union(terms) | ||||
| 			if len(tset.terms) > maxTermCount { | ||||
| 				return nil, fmt.Errorf("exceeded max term count %d", maxTermCount) | ||||
| 			} | ||||
| 		} | ||||
| 	case *TypeParam: | ||||
| 		panic("unreachable") | ||||
| 	default: | ||||
| 		// For all other types, the term set is just a single non-tilde term
 | ||||
| 		// holding the type itself.
 | ||||
| 		if u != types.Typ[types.Invalid] { | ||||
| 			tset.terms = termlist{{false, t}} | ||||
| 		} | ||||
| 	} | ||||
| 	return tset, nil | ||||
| } | ||||
| 
 | ||||
| // under is a facade for the go/types internal function of the same name. It is
 | ||||
| // used by typeterm.go.
 | ||||
| func under(t types.Type) types.Type { | ||||
| 	return t.Underlying() | ||||
| } | ||||
|  | @ -0,0 +1,163 @@ | |||
| // Copyright 2021 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.
 | ||||
| 
 | ||||
| // Code generated by copytermlist.go DO NOT EDIT.
 | ||||
| 
 | ||||
| package typeparams | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"go/types" | ||||
| ) | ||||
| 
 | ||||
| // A termlist represents the type set represented by the union
 | ||||
| // t1 ∪ y2 ∪ ... tn of the type sets of the terms t1 to tn.
 | ||||
| // A termlist is in normal form if all terms are disjoint.
 | ||||
| // termlist operations don't require the operands to be in
 | ||||
| // normal form.
 | ||||
| type termlist []*term | ||||
| 
 | ||||
| // allTermlist represents the set of all types.
 | ||||
| // It is in normal form.
 | ||||
| var allTermlist = termlist{new(term)} | ||||
| 
 | ||||
| // String prints the termlist exactly (without normalization).
 | ||||
| func (xl termlist) String() string { | ||||
| 	if len(xl) == 0 { | ||||
| 		return "∅" | ||||
| 	} | ||||
| 	var buf bytes.Buffer | ||||
| 	for i, x := range xl { | ||||
| 		if i > 0 { | ||||
| 			buf.WriteString(" ∪ ") | ||||
| 		} | ||||
| 		buf.WriteString(x.String()) | ||||
| 	} | ||||
| 	return buf.String() | ||||
| } | ||||
| 
 | ||||
| // isEmpty reports whether the termlist xl represents the empty set of types.
 | ||||
| func (xl termlist) isEmpty() bool { | ||||
| 	// If there's a non-nil term, the entire list is not empty.
 | ||||
| 	// If the termlist is in normal form, this requires at most
 | ||||
| 	// one iteration.
 | ||||
| 	for _, x := range xl { | ||||
| 		if x != nil { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // isAll reports whether the termlist xl represents the set of all types.
 | ||||
| func (xl termlist) isAll() bool { | ||||
| 	// If there's a 𝓤 term, the entire list is 𝓤.
 | ||||
| 	// If the termlist is in normal form, this requires at most
 | ||||
| 	// one iteration.
 | ||||
| 	for _, x := range xl { | ||||
| 		if x != nil && x.typ == nil { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| // norm returns the normal form of xl.
 | ||||
| func (xl termlist) norm() termlist { | ||||
| 	// Quadratic algorithm, but good enough for now.
 | ||||
| 	// TODO(gri) fix asymptotic performance
 | ||||
| 	used := make([]bool, len(xl)) | ||||
| 	var rl termlist | ||||
| 	for i, xi := range xl { | ||||
| 		if xi == nil || used[i] { | ||||
| 			continue | ||||
| 		} | ||||
| 		for j := i + 1; j < len(xl); j++ { | ||||
| 			xj := xl[j] | ||||
| 			if xj == nil || used[j] { | ||||
| 				continue | ||||
| 			} | ||||
| 			if u1, u2 := xi.union(xj); u2 == nil { | ||||
| 				// If we encounter a 𝓤 term, the entire list is 𝓤.
 | ||||
| 				// Exit early.
 | ||||
| 				// (Note that this is not just an optimization;
 | ||||
| 				// if we continue, we may end up with a 𝓤 term
 | ||||
| 				// and other terms and the result would not be
 | ||||
| 				// in normal form.)
 | ||||
| 				if u1.typ == nil { | ||||
| 					return allTermlist | ||||
| 				} | ||||
| 				xi = u1 | ||||
| 				used[j] = true // xj is now unioned into xi - ignore it in future iterations
 | ||||
| 			} | ||||
| 		} | ||||
| 		rl = append(rl, xi) | ||||
| 	} | ||||
| 	return rl | ||||
| } | ||||
| 
 | ||||
| // union returns the union xl ∪ yl.
 | ||||
| func (xl termlist) union(yl termlist) termlist { | ||||
| 	return append(xl, yl...).norm() | ||||
| } | ||||
| 
 | ||||
| // intersect returns the intersection xl ∩ yl.
 | ||||
| func (xl termlist) intersect(yl termlist) termlist { | ||||
| 	if xl.isEmpty() || yl.isEmpty() { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	// Quadratic algorithm, but good enough for now.
 | ||||
| 	// TODO(gri) fix asymptotic performance
 | ||||
| 	var rl termlist | ||||
| 	for _, x := range xl { | ||||
| 		for _, y := range yl { | ||||
| 			if r := x.intersect(y); r != nil { | ||||
| 				rl = append(rl, r) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return rl.norm() | ||||
| } | ||||
| 
 | ||||
| // equal reports whether xl and yl represent the same type set.
 | ||||
| func (xl termlist) equal(yl termlist) bool { | ||||
| 	// TODO(gri) this should be more efficient
 | ||||
| 	return xl.subsetOf(yl) && yl.subsetOf(xl) | ||||
| } | ||||
| 
 | ||||
| // includes reports whether t ∈ xl.
 | ||||
| func (xl termlist) includes(t types.Type) bool { | ||||
| 	for _, x := range xl { | ||||
| 		if x.includes(t) { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| // supersetOf reports whether y ⊆ xl.
 | ||||
| func (xl termlist) supersetOf(y *term) bool { | ||||
| 	for _, x := range xl { | ||||
| 		if y.subsetOf(x) { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| // subsetOf reports whether xl ⊆ yl.
 | ||||
| func (xl termlist) subsetOf(yl termlist) bool { | ||||
| 	if yl.isEmpty() { | ||||
| 		return xl.isEmpty() | ||||
| 	} | ||||
| 
 | ||||
| 	// each term x of xl must be a subset of yl
 | ||||
| 	for _, x := range xl { | ||||
| 		if !yl.supersetOf(x) { | ||||
| 			return false // x is not a subset yl
 | ||||
| 		} | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
							
								
								
									
										197
									
								
								vendor/golang.org/x/tools/internal/typeparams/typeparams_go117.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										197
									
								
								vendor/golang.org/x/tools/internal/typeparams/typeparams_go117.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							|  | @ -0,0 +1,197 @@ | |||
| // Copyright 2021 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.
 | ||||
| 
 | ||||
| //go:build !go1.18
 | ||||
| // +build !go1.18
 | ||||
| 
 | ||||
| package typeparams | ||||
| 
 | ||||
| import ( | ||||
| 	"go/ast" | ||||
| 	"go/token" | ||||
| 	"go/types" | ||||
| ) | ||||
| 
 | ||||
| func unsupported() { | ||||
| 	panic("type parameters are unsupported at this go version") | ||||
| } | ||||
| 
 | ||||
| // IndexListExpr is a placeholder type, as type parameters are not supported at
 | ||||
| // this Go version. Its methods panic on use.
 | ||||
| type IndexListExpr struct { | ||||
| 	ast.Expr | ||||
| 	X       ast.Expr   // expression
 | ||||
| 	Lbrack  token.Pos  // position of "["
 | ||||
| 	Indices []ast.Expr // index expressions
 | ||||
| 	Rbrack  token.Pos  // position of "]"
 | ||||
| } | ||||
| 
 | ||||
| // ForTypeSpec returns an empty field list, as type parameters on not supported
 | ||||
| // at this Go version.
 | ||||
| func ForTypeSpec(*ast.TypeSpec) *ast.FieldList { | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // ForFuncType returns an empty field list, as type parameters are not
 | ||||
| // supported at this Go version.
 | ||||
| func ForFuncType(*ast.FuncType) *ast.FieldList { | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // TypeParam is a placeholder type, as type parameters are not supported at
 | ||||
| // this Go version. Its methods panic on use.
 | ||||
| type TypeParam struct{ types.Type } | ||||
| 
 | ||||
| func (*TypeParam) Index() int             { unsupported(); return 0 } | ||||
| func (*TypeParam) Constraint() types.Type { unsupported(); return nil } | ||||
| func (*TypeParam) Obj() *types.TypeName   { unsupported(); return nil } | ||||
| 
 | ||||
| // TypeParamList is a placeholder for an empty type parameter list.
 | ||||
| type TypeParamList struct{} | ||||
| 
 | ||||
| func (*TypeParamList) Len() int          { return 0 } | ||||
| func (*TypeParamList) At(int) *TypeParam { unsupported(); return nil } | ||||
| 
 | ||||
| // TypeList is a placeholder for an empty type list.
 | ||||
| type TypeList struct{} | ||||
| 
 | ||||
| func (*TypeList) Len() int          { return 0 } | ||||
| func (*TypeList) At(int) types.Type { unsupported(); return nil } | ||||
| 
 | ||||
| // NewTypeParam is unsupported at this Go version, and panics.
 | ||||
| func NewTypeParam(name *types.TypeName, constraint types.Type) *TypeParam { | ||||
| 	unsupported() | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // SetTypeParamConstraint is unsupported at this Go version, and panics.
 | ||||
| func SetTypeParamConstraint(tparam *TypeParam, constraint types.Type) { | ||||
| 	unsupported() | ||||
| } | ||||
| 
 | ||||
| // NewSignatureType calls types.NewSignature, panicking if recvTypeParams or
 | ||||
| // typeParams is non-empty.
 | ||||
| func NewSignatureType(recv *types.Var, recvTypeParams, typeParams []*TypeParam, params, results *types.Tuple, variadic bool) *types.Signature { | ||||
| 	if len(recvTypeParams) != 0 || len(typeParams) != 0 { | ||||
| 		panic("signatures cannot have type parameters at this Go version") | ||||
| 	} | ||||
| 	return types.NewSignature(recv, params, results, variadic) | ||||
| } | ||||
| 
 | ||||
| // ForSignature returns an empty slice.
 | ||||
| func ForSignature(*types.Signature) *TypeParamList { | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // RecvTypeParams returns a nil slice.
 | ||||
| func RecvTypeParams(sig *types.Signature) *TypeParamList { | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // IsComparable returns false, as no interfaces are type-restricted at this Go
 | ||||
| // version.
 | ||||
| func IsComparable(*types.Interface) bool { | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| // IsMethodSet returns true, as no interfaces are type-restricted at this Go
 | ||||
| // version.
 | ||||
| func IsMethodSet(*types.Interface) bool { | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // IsImplicit returns false, as no interfaces are implicit at this Go version.
 | ||||
| func IsImplicit(*types.Interface) bool { | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| // MarkImplicit does nothing, because this Go version does not have implicit
 | ||||
| // interfaces.
 | ||||
| func MarkImplicit(*types.Interface) {} | ||||
| 
 | ||||
| // ForNamed returns an empty type parameter list, as type parameters are not
 | ||||
| // supported at this Go version.
 | ||||
| func ForNamed(*types.Named) *TypeParamList { | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // SetForNamed panics if tparams is non-empty.
 | ||||
| func SetForNamed(_ *types.Named, tparams []*TypeParam) { | ||||
| 	if len(tparams) > 0 { | ||||
| 		unsupported() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // NamedTypeArgs returns nil.
 | ||||
| func NamedTypeArgs(*types.Named) *TypeList { | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // NamedTypeOrigin is the identity method at this Go version.
 | ||||
| func NamedTypeOrigin(named *types.Named) types.Type { | ||||
| 	return named | ||||
| } | ||||
| 
 | ||||
| // Term holds information about a structural type restriction.
 | ||||
| type Term struct { | ||||
| 	tilde bool | ||||
| 	typ   types.Type | ||||
| } | ||||
| 
 | ||||
| func (m *Term) Tilde() bool      { return m.tilde } | ||||
| func (m *Term) Type() types.Type { return m.typ } | ||||
| func (m *Term) String() string { | ||||
| 	pre := "" | ||||
| 	if m.tilde { | ||||
| 		pre = "~" | ||||
| 	} | ||||
| 	return pre + m.typ.String() | ||||
| } | ||||
| 
 | ||||
| // NewTerm is unsupported at this Go version, and panics.
 | ||||
| func NewTerm(tilde bool, typ types.Type) *Term { | ||||
| 	return &Term{tilde, typ} | ||||
| } | ||||
| 
 | ||||
| // Union is a placeholder type, as type parameters are not supported at this Go
 | ||||
| // version. Its methods panic on use.
 | ||||
| type Union struct{ types.Type } | ||||
| 
 | ||||
| func (*Union) Len() int         { return 0 } | ||||
| func (*Union) Term(i int) *Term { unsupported(); return nil } | ||||
| 
 | ||||
| // NewUnion is unsupported at this Go version, and panics.
 | ||||
| func NewUnion(terms []*Term) *Union { | ||||
| 	unsupported() | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // InitInstanceInfo is a noop at this Go version.
 | ||||
| func InitInstanceInfo(*types.Info) {} | ||||
| 
 | ||||
| // Instance is a placeholder type, as type parameters are not supported at this
 | ||||
| // Go version.
 | ||||
| type Instance struct { | ||||
| 	TypeArgs *TypeList | ||||
| 	Type     types.Type | ||||
| } | ||||
| 
 | ||||
| // GetInstances returns a nil map, as type parameters are not supported at this
 | ||||
| // Go version.
 | ||||
| func GetInstances(info *types.Info) map[*ast.Ident]Instance { return nil } | ||||
| 
 | ||||
| // Context is a placeholder type, as type parameters are not supported at
 | ||||
| // this Go version.
 | ||||
| type Context struct{} | ||||
| 
 | ||||
| // NewContext returns a placeholder Context instance.
 | ||||
| func NewContext() *Context { | ||||
| 	return &Context{} | ||||
| } | ||||
| 
 | ||||
| // Instantiate is unsupported on this Go version, and panics.
 | ||||
| func Instantiate(ctxt *Context, typ types.Type, targs []types.Type, validate bool) (types.Type, error) { | ||||
| 	unsupported() | ||||
| 	return nil, nil | ||||
| } | ||||
							
								
								
									
										151
									
								
								vendor/golang.org/x/tools/internal/typeparams/typeparams_go118.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										151
									
								
								vendor/golang.org/x/tools/internal/typeparams/typeparams_go118.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							|  | @ -0,0 +1,151 @@ | |||
| // Copyright 2021 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.
 | ||||
| 
 | ||||
| //go:build go1.18
 | ||||
| // +build go1.18
 | ||||
| 
 | ||||
| package typeparams | ||||
| 
 | ||||
| import ( | ||||
| 	"go/ast" | ||||
| 	"go/types" | ||||
| ) | ||||
| 
 | ||||
| // IndexListExpr is an alias for ast.IndexListExpr.
 | ||||
| type IndexListExpr = ast.IndexListExpr | ||||
| 
 | ||||
| // ForTypeSpec returns n.TypeParams.
 | ||||
| func ForTypeSpec(n *ast.TypeSpec) *ast.FieldList { | ||||
| 	if n == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return n.TypeParams | ||||
| } | ||||
| 
 | ||||
| // ForFuncType returns n.TypeParams.
 | ||||
| func ForFuncType(n *ast.FuncType) *ast.FieldList { | ||||
| 	if n == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return n.TypeParams | ||||
| } | ||||
| 
 | ||||
| // TypeParam is an alias for types.TypeParam
 | ||||
| type TypeParam = types.TypeParam | ||||
| 
 | ||||
| // TypeParamList is an alias for types.TypeParamList
 | ||||
| type TypeParamList = types.TypeParamList | ||||
| 
 | ||||
| // TypeList is an alias for types.TypeList
 | ||||
| type TypeList = types.TypeList | ||||
| 
 | ||||
| // NewTypeParam calls types.NewTypeParam.
 | ||||
| func NewTypeParam(name *types.TypeName, constraint types.Type) *TypeParam { | ||||
| 	return types.NewTypeParam(name, constraint) | ||||
| } | ||||
| 
 | ||||
| // SetTypeParamConstraint calls tparam.SetConstraint(constraint).
 | ||||
| func SetTypeParamConstraint(tparam *TypeParam, constraint types.Type) { | ||||
| 	tparam.SetConstraint(constraint) | ||||
| } | ||||
| 
 | ||||
| // NewSignatureType calls types.NewSignatureType.
 | ||||
| func NewSignatureType(recv *types.Var, recvTypeParams, typeParams []*TypeParam, params, results *types.Tuple, variadic bool) *types.Signature { | ||||
| 	return types.NewSignatureType(recv, recvTypeParams, typeParams, params, results, variadic) | ||||
| } | ||||
| 
 | ||||
| // ForSignature returns sig.TypeParams()
 | ||||
| func ForSignature(sig *types.Signature) *TypeParamList { | ||||
| 	return sig.TypeParams() | ||||
| } | ||||
| 
 | ||||
| // RecvTypeParams returns sig.RecvTypeParams().
 | ||||
| func RecvTypeParams(sig *types.Signature) *TypeParamList { | ||||
| 	return sig.RecvTypeParams() | ||||
| } | ||||
| 
 | ||||
| // IsComparable calls iface.IsComparable().
 | ||||
| func IsComparable(iface *types.Interface) bool { | ||||
| 	return iface.IsComparable() | ||||
| } | ||||
| 
 | ||||
| // IsMethodSet calls iface.IsMethodSet().
 | ||||
| func IsMethodSet(iface *types.Interface) bool { | ||||
| 	return iface.IsMethodSet() | ||||
| } | ||||
| 
 | ||||
| // IsImplicit calls iface.IsImplicit().
 | ||||
| func IsImplicit(iface *types.Interface) bool { | ||||
| 	return iface.IsImplicit() | ||||
| } | ||||
| 
 | ||||
| // MarkImplicit calls iface.MarkImplicit().
 | ||||
| func MarkImplicit(iface *types.Interface) { | ||||
| 	iface.MarkImplicit() | ||||
| } | ||||
| 
 | ||||
| // ForNamed extracts the (possibly empty) type parameter object list from
 | ||||
| // named.
 | ||||
| func ForNamed(named *types.Named) *TypeParamList { | ||||
| 	return named.TypeParams() | ||||
| } | ||||
| 
 | ||||
| // SetForNamed sets the type params tparams on n. Each tparam must be of
 | ||||
| // dynamic type *types.TypeParam.
 | ||||
| func SetForNamed(n *types.Named, tparams []*TypeParam) { | ||||
| 	n.SetTypeParams(tparams) | ||||
| } | ||||
| 
 | ||||
| // NamedTypeArgs returns named.TypeArgs().
 | ||||
| func NamedTypeArgs(named *types.Named) *TypeList { | ||||
| 	return named.TypeArgs() | ||||
| } | ||||
| 
 | ||||
| // NamedTypeOrigin returns named.Orig().
 | ||||
| func NamedTypeOrigin(named *types.Named) types.Type { | ||||
| 	return named.Origin() | ||||
| } | ||||
| 
 | ||||
| // Term is an alias for types.Term.
 | ||||
| type Term = types.Term | ||||
| 
 | ||||
| // NewTerm calls types.NewTerm.
 | ||||
| func NewTerm(tilde bool, typ types.Type) *Term { | ||||
| 	return types.NewTerm(tilde, typ) | ||||
| } | ||||
| 
 | ||||
| // Union is an alias for types.Union
 | ||||
| type Union = types.Union | ||||
| 
 | ||||
| // NewUnion calls types.NewUnion.
 | ||||
| func NewUnion(terms []*Term) *Union { | ||||
| 	return types.NewUnion(terms) | ||||
| } | ||||
| 
 | ||||
| // InitInstanceInfo initializes info to record information about type and
 | ||||
| // function instances.
 | ||||
| func InitInstanceInfo(info *types.Info) { | ||||
| 	info.Instances = make(map[*ast.Ident]types.Instance) | ||||
| } | ||||
| 
 | ||||
| // Instance is an alias for types.Instance.
 | ||||
| type Instance = types.Instance | ||||
| 
 | ||||
| // GetInstances returns info.Instances.
 | ||||
| func GetInstances(info *types.Info) map[*ast.Ident]Instance { | ||||
| 	return info.Instances | ||||
| } | ||||
| 
 | ||||
| // Context is an alias for types.Context.
 | ||||
| type Context = types.Context | ||||
| 
 | ||||
| // NewContext calls types.NewContext.
 | ||||
| func NewContext() *Context { | ||||
| 	return types.NewContext() | ||||
| } | ||||
| 
 | ||||
| // Instantiate calls types.Instantiate.
 | ||||
| func Instantiate(ctxt *Context, typ types.Type, targs []types.Type, validate bool) (types.Type, error) { | ||||
| 	return types.Instantiate(ctxt, typ, targs, validate) | ||||
| } | ||||
|  | @ -0,0 +1,170 @@ | |||
| // Copyright 2021 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.
 | ||||
| 
 | ||||
| // Code generated by copytermlist.go DO NOT EDIT.
 | ||||
| 
 | ||||
| package typeparams | ||||
| 
 | ||||
| import "go/types" | ||||
| 
 | ||||
| // A term describes elementary type sets:
 | ||||
| //
 | ||||
| //   ∅:  (*term)(nil)     == ∅                      // set of no types (empty set)
 | ||||
| //   𝓤:  &term{}          == 𝓤                      // set of all types (𝓤niverse)
 | ||||
| //   T:  &term{false, T}  == {T}                    // set of type T
 | ||||
| //  ~t:  &term{true, t}   == {t' | under(t') == t}  // set of types with underlying type t
 | ||||
| //
 | ||||
| type term struct { | ||||
| 	tilde bool // valid if typ != nil
 | ||||
| 	typ   types.Type | ||||
| } | ||||
| 
 | ||||
| func (x *term) String() string { | ||||
| 	switch { | ||||
| 	case x == nil: | ||||
| 		return "∅" | ||||
| 	case x.typ == nil: | ||||
| 		return "𝓤" | ||||
| 	case x.tilde: | ||||
| 		return "~" + x.typ.String() | ||||
| 	default: | ||||
| 		return x.typ.String() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // equal reports whether x and y represent the same type set.
 | ||||
| func (x *term) equal(y *term) bool { | ||||
| 	// easy cases
 | ||||
| 	switch { | ||||
| 	case x == nil || y == nil: | ||||
| 		return x == y | ||||
| 	case x.typ == nil || y.typ == nil: | ||||
| 		return x.typ == y.typ | ||||
| 	} | ||||
| 	// ∅ ⊂ x, y ⊂ 𝓤
 | ||||
| 
 | ||||
| 	return x.tilde == y.tilde && types.Identical(x.typ, y.typ) | ||||
| } | ||||
| 
 | ||||
| // union returns the union x ∪ y: zero, one, or two non-nil terms.
 | ||||
| func (x *term) union(y *term) (_, _ *term) { | ||||
| 	// easy cases
 | ||||
| 	switch { | ||||
| 	case x == nil && y == nil: | ||||
| 		return nil, nil // ∅ ∪ ∅ == ∅
 | ||||
| 	case x == nil: | ||||
| 		return y, nil // ∅ ∪ y == y
 | ||||
| 	case y == nil: | ||||
| 		return x, nil // x ∪ ∅ == x
 | ||||
| 	case x.typ == nil: | ||||
| 		return x, nil // 𝓤 ∪ y == 𝓤
 | ||||
| 	case y.typ == nil: | ||||
| 		return y, nil // x ∪ 𝓤 == 𝓤
 | ||||
| 	} | ||||
| 	// ∅ ⊂ x, y ⊂ 𝓤
 | ||||
| 
 | ||||
| 	if x.disjoint(y) { | ||||
| 		return x, y // x ∪ y == (x, y) if x ∩ y == ∅
 | ||||
| 	} | ||||
| 	// x.typ == y.typ
 | ||||
| 
 | ||||
| 	// ~t ∪ ~t == ~t
 | ||||
| 	// ~t ∪  T == ~t
 | ||||
| 	//  T ∪ ~t == ~t
 | ||||
| 	//  T ∪  T ==  T
 | ||||
| 	if x.tilde || !y.tilde { | ||||
| 		return x, nil | ||||
| 	} | ||||
| 	return y, nil | ||||
| } | ||||
| 
 | ||||
| // intersect returns the intersection x ∩ y.
 | ||||
| func (x *term) intersect(y *term) *term { | ||||
| 	// easy cases
 | ||||
| 	switch { | ||||
| 	case x == nil || y == nil: | ||||
| 		return nil // ∅ ∩ y == ∅ and ∩ ∅ == ∅
 | ||||
| 	case x.typ == nil: | ||||
| 		return y // 𝓤 ∩ y == y
 | ||||
| 	case y.typ == nil: | ||||
| 		return x // x ∩ 𝓤 == x
 | ||||
| 	} | ||||
| 	// ∅ ⊂ x, y ⊂ 𝓤
 | ||||
| 
 | ||||
| 	if x.disjoint(y) { | ||||
| 		return nil // x ∩ y == ∅ if x ∩ y == ∅
 | ||||
| 	} | ||||
| 	// x.typ == y.typ
 | ||||
| 
 | ||||
| 	// ~t ∩ ~t == ~t
 | ||||
| 	// ~t ∩  T ==  T
 | ||||
| 	//  T ∩ ~t ==  T
 | ||||
| 	//  T ∩  T ==  T
 | ||||
| 	if !x.tilde || y.tilde { | ||||
| 		return x | ||||
| 	} | ||||
| 	return y | ||||
| } | ||||
| 
 | ||||
| // includes reports whether t ∈ x.
 | ||||
| func (x *term) includes(t types.Type) bool { | ||||
| 	// easy cases
 | ||||
| 	switch { | ||||
| 	case x == nil: | ||||
| 		return false // t ∈ ∅ == false
 | ||||
| 	case x.typ == nil: | ||||
| 		return true // t ∈ 𝓤 == true
 | ||||
| 	} | ||||
| 	// ∅ ⊂ x ⊂ 𝓤
 | ||||
| 
 | ||||
| 	u := t | ||||
| 	if x.tilde { | ||||
| 		u = under(u) | ||||
| 	} | ||||
| 	return types.Identical(x.typ, u) | ||||
| } | ||||
| 
 | ||||
| // subsetOf reports whether x ⊆ y.
 | ||||
| func (x *term) subsetOf(y *term) bool { | ||||
| 	// easy cases
 | ||||
| 	switch { | ||||
| 	case x == nil: | ||||
| 		return true // ∅ ⊆ y == true
 | ||||
| 	case y == nil: | ||||
| 		return false // x ⊆ ∅ == false since x != ∅
 | ||||
| 	case y.typ == nil: | ||||
| 		return true // x ⊆ 𝓤 == true
 | ||||
| 	case x.typ == nil: | ||||
| 		return false // 𝓤 ⊆ y == false since y != 𝓤
 | ||||
| 	} | ||||
| 	// ∅ ⊂ x, y ⊂ 𝓤
 | ||||
| 
 | ||||
| 	if x.disjoint(y) { | ||||
| 		return false // x ⊆ y == false if x ∩ y == ∅
 | ||||
| 	} | ||||
| 	// x.typ == y.typ
 | ||||
| 
 | ||||
| 	// ~t ⊆ ~t == true
 | ||||
| 	// ~t ⊆ T == false
 | ||||
| 	//  T ⊆ ~t == true
 | ||||
| 	//  T ⊆  T == true
 | ||||
| 	return !x.tilde || y.tilde | ||||
| } | ||||
| 
 | ||||
| // disjoint reports whether x ∩ y == ∅.
 | ||||
| // x.typ and y.typ must not be nil.
 | ||||
| func (x *term) disjoint(y *term) bool { | ||||
| 	if debug && (x.typ == nil || y.typ == nil) { | ||||
| 		panic("invalid argument(s)") | ||||
| 	} | ||||
| 	ux := x.typ | ||||
| 	if y.tilde { | ||||
| 		ux = under(ux) | ||||
| 	} | ||||
| 	uy := y.typ | ||||
| 	if x.tilde { | ||||
| 		uy = under(uy) | ||||
| 	} | ||||
| 	return !types.Identical(ux, uy) | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue