From 5329f28580c0eaadea638245df1fcf48e3f04025 Mon Sep 17 00:00:00 2001 From: Li Jie Date: Fri, 14 Feb 2025 17:18:13 +0800 Subject: [PATCH] runtime: testing runtime --- compiler/internal/build/build.go | 13 +- runtime/internal/clite/libuv/README.md | 44 + .../internal/clite/libuv/_demo/async/async.go | 41 + .../clite/libuv/_demo/async_fs/async_fs.go | 116 ++ .../clite/libuv/_demo/async_fs/example.txt | 1 + .../libuv/_demo/echo_server/echo_server.go | 112 ++ runtime/internal/clite/libuv/_wrap/libuv.c | 5 + runtime/internal/clite/libuv/async.go | 50 + runtime/internal/clite/libuv/check.go | 27 + runtime/internal/clite/libuv/error.go | 118 ++ runtime/internal/clite/libuv/fs.go | 307 ++++ runtime/internal/clite/libuv/idle.go | 27 + runtime/internal/clite/libuv/libuv.go | 276 ++++ runtime/internal/clite/libuv/net.go | 543 +++++++ runtime/internal/clite/libuv/signal.go | 42 + runtime/internal/clite/libuv/thread.go | 78 ++ runtime/internal/clite/libuv/timer.go | 56 + runtime/internal/clite/net/net.go | 223 +++ runtime/internal/clite/os/os.go | 3 + runtime/internal/clite/syscall/syscall.go | 6 +- runtime/internal/lib/crypto/hmac/hmac.go | 4 +- runtime/internal/lib/crypto/md5/md5.go | 4 +- runtime/internal/lib/crypto/rand/rand.go | 4 +- runtime/internal/lib/crypto/sha1/sha1.go | 4 +- runtime/internal/lib/crypto/sha256/sha256.go | 4 +- runtime/internal/lib/crypto/sha512/sha512.go | 4 +- runtime/internal/lib/fmt/errors.go | 78 -- runtime/internal/lib/fmt/fmt.go | 24 - runtime/internal/lib/fmt/format.go | 594 -------- runtime/internal/lib/fmt/print.go | 1243 ----------------- runtime/internal/lib/hash/crc32/crc32.go | 4 +- runtime/internal/lib/internal/abi/abi.go | 14 +- .../internal/lib/internal/bytealg/bytealg.go | 4 +- runtime/internal/lib/internal/cpu/cpu.go | 1 + runtime/internal/lib/internal/cpu/cpu_x86.go | 40 + runtime/internal/lib/internal/fmtsort/sort.go | 4 +- .../internal/lib/internal/godebug/godebug.go | 13 + runtime/internal/lib/internal/itoa/itoa.go | 4 +- .../internal/lib/internal/oserror/errors.go | 4 +- runtime/internal/lib/internal/poll/poll.go | 45 + .../syscall/execenv/execenv_default.go | 4 +- .../syscall/execenv/execenv_windows.go | 4 +- .../internal/syscall/unix/_unix/fcntl_unix.c | 33 + ...{nonblocking_js.go => at_sysnum_darwin.go} | 15 +- .../internal/syscall/unix/at_sysnum_linux.go | 19 + .../lib/internal/syscall/unix/constants.go | 18 + .../lib/internal/syscall/unix/fcntl_unix.go | 23 + .../lib/internal/syscall/unix/nofollow_bsd.go | 14 + .../internal/syscall/unix/nofollow_netbsd.go | 10 + .../internal/syscall/unix/nofollow_posix.go | 22 + .../internal/syscall/unix/nonblocking_unix.go | 23 - .../syscall/unix/nonblocking_wasip1.go | 37 - .../lib/internal/syscall/unix/unix.go | 6 +- runtime/internal/lib/math/big/int.go | 4 +- runtime/internal/lib/math/math.go | 2 +- runtime/internal/lib/math/rand/rand.go | 4 +- runtime/internal/lib/os/dir.go | 180 +++ runtime/internal/lib/os/exec/exec.go | 4 +- runtime/internal/lib/os/exec_posix.go | 6 +- runtime/internal/lib/os/os.go | 4 +- runtime/internal/lib/os/path.go | 4 + runtime/internal/lib/os/removeall_at.go | 222 +++ runtime/internal/lib/os/removeall_noat.go | 142 ++ runtime/internal/lib/os/signal/signal.go | 25 + runtime/internal/lib/os/types_unix.go | 2 +- runtime/internal/lib/runtime/debug/debug.go | 5 + runtime/internal/lib/runtime/extern.go | 4 + .../lib/runtime/internal/syscall/syscall.go | 6 + runtime/internal/lib/runtime/pprof/pprof.go | 24 + runtime/internal/lib/runtime/runtime.go | 43 +- runtime/internal/lib/runtime/runtime2.go | 34 + runtime/internal/lib/runtime/runtime_gc.go | 9 + runtime/internal/lib/runtime/runtime_nogc.go | 7 + runtime/internal/lib/runtime/trace/trace.go | 5 + runtime/internal/lib/sync/atomic/type.go | 103 ++ runtime/internal/lib/sync/sync.go | 4 +- runtime/internal/lib/syscall/syscall.go | 25 + .../internal/lib/syscall/syscall_darwin.go | 5 + runtime/internal/lib/syscall/syscall_linux.go | 9 + runtime/internal/lib/syscall/syscall_unix.go | 10 + runtime/internal/lib/time/sleep.go | 93 +- runtime/internal/lib/time/tick.go | 36 + runtime/internal/lib/time/time.go | 4 + 83 files changed, 3376 insertions(+), 2061 deletions(-) create mode 100644 runtime/internal/clite/libuv/README.md create mode 100644 runtime/internal/clite/libuv/_demo/async/async.go create mode 100644 runtime/internal/clite/libuv/_demo/async_fs/async_fs.go create mode 100755 runtime/internal/clite/libuv/_demo/async_fs/example.txt create mode 100644 runtime/internal/clite/libuv/_demo/echo_server/echo_server.go create mode 100644 runtime/internal/clite/libuv/_wrap/libuv.c create mode 100644 runtime/internal/clite/libuv/async.go create mode 100644 runtime/internal/clite/libuv/check.go create mode 100644 runtime/internal/clite/libuv/error.go create mode 100644 runtime/internal/clite/libuv/fs.go create mode 100644 runtime/internal/clite/libuv/idle.go create mode 100644 runtime/internal/clite/libuv/libuv.go create mode 100644 runtime/internal/clite/libuv/net.go create mode 100644 runtime/internal/clite/libuv/signal.go create mode 100644 runtime/internal/clite/libuv/thread.go create mode 100644 runtime/internal/clite/libuv/timer.go create mode 100644 runtime/internal/clite/net/net.go delete mode 100644 runtime/internal/lib/fmt/errors.go delete mode 100644 runtime/internal/lib/fmt/fmt.go delete mode 100644 runtime/internal/lib/fmt/format.go delete mode 100644 runtime/internal/lib/fmt/print.go create mode 100644 runtime/internal/lib/internal/cpu/cpu.go create mode 100644 runtime/internal/lib/internal/cpu/cpu_x86.go create mode 100644 runtime/internal/lib/internal/godebug/godebug.go create mode 100644 runtime/internal/lib/internal/poll/poll.go create mode 100644 runtime/internal/lib/internal/syscall/unix/_unix/fcntl_unix.c rename runtime/internal/lib/internal/syscall/unix/{nonblocking_js.go => at_sysnum_darwin.go} (53%) create mode 100644 runtime/internal/lib/internal/syscall/unix/at_sysnum_linux.go create mode 100644 runtime/internal/lib/internal/syscall/unix/constants.go create mode 100644 runtime/internal/lib/internal/syscall/unix/fcntl_unix.go create mode 100644 runtime/internal/lib/internal/syscall/unix/nofollow_bsd.go create mode 100644 runtime/internal/lib/internal/syscall/unix/nofollow_netbsd.go create mode 100644 runtime/internal/lib/internal/syscall/unix/nofollow_posix.go delete mode 100644 runtime/internal/lib/internal/syscall/unix/nonblocking_unix.go delete mode 100644 runtime/internal/lib/internal/syscall/unix/nonblocking_wasip1.go create mode 100644 runtime/internal/lib/os/dir.go create mode 100644 runtime/internal/lib/os/removeall_at.go create mode 100644 runtime/internal/lib/os/removeall_noat.go create mode 100644 runtime/internal/lib/os/signal/signal.go create mode 100644 runtime/internal/lib/runtime/debug/debug.go create mode 100644 runtime/internal/lib/runtime/internal/syscall/syscall.go create mode 100644 runtime/internal/lib/runtime/pprof/pprof.go create mode 100644 runtime/internal/lib/runtime/runtime_gc.go create mode 100644 runtime/internal/lib/runtime/runtime_nogc.go create mode 100644 runtime/internal/lib/runtime/trace/trace.go create mode 100644 runtime/internal/lib/sync/atomic/type.go create mode 100644 runtime/internal/lib/syscall/syscall_darwin.go create mode 100644 runtime/internal/lib/time/tick.go diff --git a/compiler/internal/build/build.go b/compiler/internal/build/build.go index abae1436..f80d8daf 100644 --- a/compiler/internal/build/build.go +++ b/compiler/internal/build/build.go @@ -536,6 +536,9 @@ source_filename = "main" %s declare void @"%s.init"() declare void @"%s.main"() +define weak void @runtime.init() { + ret void +} ; TODO(lijie): workaround for syscall patch define weak void @"syscall.init"() { @@ -548,6 +551,7 @@ _llgo_0: store i32 %%0, ptr @__llgo_argc, align 4 store ptr %%1, ptr @__llgo_argv, align 8 %s + call void @runtime.init() call void @"%s.init"() call void @"%s.main"() ret i32 0 @@ -893,14 +897,16 @@ var hasAltPkg = map[string]none{ "crypto/sha256": {}, "crypto/sha512": {}, "crypto/subtle": {}, - "fmt": {}, "go/parser": {}, "hash/crc32": {}, "internal/abi": {}, "internal/bytealg": {}, + "internal/cpu": {}, "internal/itoa": {}, "internal/filepathlite": {}, + "internal/godebug": {}, "internal/oserror": {}, + "internal/poll": {}, "internal/race": {}, "internal/reflectlite": {}, "internal/stringslite": {}, @@ -917,7 +923,12 @@ var hasAltPkg = map[string]none{ "time": {}, "os": {}, "os/exec": {}, + "os/signal": {}, "runtime": {}, + "runtime/debug": {}, + "runtime/pprof": {}, + "runtime/trace": {}, + "runtime/internal/syscall": {}, "io": {}, } diff --git a/runtime/internal/clite/libuv/README.md b/runtime/internal/clite/libuv/README.md new file mode 100644 index 00000000..30f2ed5b --- /dev/null +++ b/runtime/internal/clite/libuv/README.md @@ -0,0 +1,44 @@ +LLGo wrapper of libuv +===== + +## How to install + +### on macOS (Homebrew) + +```sh +brew install libuv +``` + +### on Linux (Debian/Ubuntu) + +```sh +apt-get install -y libuv1-dev +``` + +### on Linux (CentOS/RHEL) + +```sh +yum install -y libuv-devel +``` + +### on Linux (Arch Linux) + +```sh +pacman -S libuv +``` + +## Demos + +The `_demo` directory contains our demos (it start with `_` to prevent the `go` command from compiling it): + +* [async_fs](_demo/async_fs/async_fs.go): a simple async file read demo +* [echo_server](_demo/echo_server/echo_server.go): a basic async tcp echo server + +### How to run demos + +To run the demos in directory `_demo`: + +```sh +cd # eg. cd _demo/sqlitedemo +llgo run . +``` diff --git a/runtime/internal/clite/libuv/_demo/async/async.go b/runtime/internal/clite/libuv/_demo/async/async.go new file mode 100644 index 00000000..eb97ba0d --- /dev/null +++ b/runtime/internal/clite/libuv/_demo/async/async.go @@ -0,0 +1,41 @@ +package main + +import ( + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/libuv" +) + +func ensure(b bool, msg string) { + if !b { + panic(msg) + } +} + +func main() { + loop := libuv.LoopNew() + defer loop.Close() + + a := &libuv.Async{} + r := loop.Async(a, func(a *libuv.Async) { + println("async callback") + a.Close(nil) // or loop.Stop() + }) + ensure(r == 0, "Async failed") + + go func() { + println("begin async task") + c.Usleep(100 * 1000) + println("send async event") + ensure(a.Send() == 0, "Send failed") + }() + + loop.Run(libuv.RUN_DEFAULT) + println("done") +} + +/*Expected Output: +begin async task +send async event +async callback +done +*/ diff --git a/runtime/internal/clite/libuv/_demo/async_fs/async_fs.go b/runtime/internal/clite/libuv/_demo/async_fs/async_fs.go new file mode 100644 index 00000000..36acff75 --- /dev/null +++ b/runtime/internal/clite/libuv/_demo/async_fs/async_fs.go @@ -0,0 +1,116 @@ +package main + +import ( + "unsafe" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/libuv" + "github.com/goplus/llgo/c/os" +) + +const BUFFER_SIZE = 1024 + +var ( + loop *libuv.Loop + openReq libuv.Fs + closeReq libuv.Fs + + buffer [BUFFER_SIZE]c.Char + iov libuv.Buf + file libuv.File +) + +func main() { + // Print the libuv version + c.Printf(c.Str("libuv version: %d\n"), libuv.Version()) + + // Initialize the loop + loop = libuv.DefaultLoop() + + // Open the file + libuv.FsOpen(loop, &openReq, c.Str("example.txt"), os.O_RDONLY, 0, onOpen) + + // Run the loop + result := loop.Run(libuv.RUN_DEFAULT) + + if result != 0 { + c.Fprintf(c.Stderr, c.Str("Error in Run: %s\n"), libuv.Strerror(libuv.Errno(result))) + } + + // Cleanup + defer cleanup() +} + +func onOpen(req *libuv.Fs) { + // Check for errors + if req.GetResult() < 0 { + c.Fprintf(c.Stderr, c.Str("Error opening file: %s\n"), libuv.Strerror(libuv.Errno(req.GetResult()))) + loop.Close() + return + } + + // Store the file descriptor + file = libuv.File(req.GetResult()) + + // Init buffer + iov = libuv.InitBuf((*c.Char)(unsafe.Pointer(&buffer[0])), c.Uint(unsafe.Sizeof(buffer))) + + // Read the file + readFile() + +} + +func readFile() { + // Initialize the request every time + var readReq libuv.Fs + + // Read the file + readRes := libuv.FsRead(loop, &readReq, file, &iov, 1, -1, onRead) + if readRes != 0 { + c.Printf(c.Str("Error in FsRead: %s (code: %d)\n"), libuv.Strerror(libuv.Errno(readRes)), readRes) + readReq.ReqCleanup() + loop.Close() + } +} + +func onRead(req *libuv.Fs) { + // Cleanup the request + defer req.ReqCleanup() + // Check for errors + if req.GetResult() < 0 { + c.Fprintf(c.Stderr, c.Str("Read error: %s\n"), libuv.Strerror(libuv.Errno(req.GetResult()))) + } else if req.GetResult() == 0 { + // Close the file + closeRes := libuv.FsClose(loop, &closeReq, libuv.File(openReq.GetResult()), onClose) + if closeRes != 0 { + c.Printf(c.Str("Error in FsClose: %s (code: %d)\n"), libuv.Strerror(libuv.Errno(closeRes)), closeRes) + loop.Close() + return + } + } else { + c.Printf(c.Str("Read %d bytes\n"), req.GetResult()) + c.Printf(c.Str("Read content: %.*s\n"), req.GetResult(), (*c.Char)(unsafe.Pointer(&buffer[0]))) + // Read the file again + readFile() + } +} + +func onClose(req *libuv.Fs) { + // Check for errors + if req.GetResult() < 0 { + c.Fprintf(c.Stderr, c.Str("Error closing file: %s\n"), libuv.Strerror(libuv.Errno(req.GetResult()))) + } else { + c.Printf(c.Str("\nFile closed successfully.\n")) + } +} + +func cleanup() { + // Cleanup the requests + openReq.ReqCleanup() + closeReq.ReqCleanup() + // Close the loop + result := loop.Close() + if result != 0 { + c.Fprintf(c.Stderr, c.Str("Error in LoopClose: %s\n"), libuv.Strerror(libuv.Errno(result))) + } +} diff --git a/runtime/internal/clite/libuv/_demo/async_fs/example.txt b/runtime/internal/clite/libuv/_demo/async_fs/example.txt new file mode 100755 index 00000000..190a1803 --- /dev/null +++ b/runtime/internal/clite/libuv/_demo/async_fs/example.txt @@ -0,0 +1 @@ +123 diff --git a/runtime/internal/clite/libuv/_demo/echo_server/echo_server.go b/runtime/internal/clite/libuv/_demo/echo_server/echo_server.go new file mode 100644 index 00000000..9a208a76 --- /dev/null +++ b/runtime/internal/clite/libuv/_demo/echo_server/echo_server.go @@ -0,0 +1,112 @@ +package main + +import ( + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/libuv" + "github.com/goplus/llgo/c/net" +) + +var DEFAULT_PORT c.Int = 8080 +var DEFAULT_BACKLOG c.Int = 128 + +type WriteReq struct { + Req libuv.Write + Buf libuv.Buf +} + +func main() { + // Initialize the default event loop + var loop = libuv.DefaultLoop() + + // Initialize a TCP server + server := &libuv.Tcp{} + libuv.InitTcp(loop, server) + + // Set up the address to bind the server to + var addr net.SockaddrIn + libuv.Ip4Addr(c.Str("0.0.0.0"), DEFAULT_PORT, &addr) + c.Printf(c.Str("Listening on %s:%d\n"), c.Str("0.0.0.0"), DEFAULT_PORT) + + // Bind the server to the specified address and port + server.Bind((*net.SockAddr)(c.Pointer(&addr)), 0) + res := (*libuv.Stream)(server).Listen(DEFAULT_BACKLOG, OnNewConnection) + if res != 0 { + c.Fprintf(c.Stderr, c.Str("Listen error: %s\n"), libuv.Strerror(libuv.Errno(res))) + return + } + + // Start listening for incoming connections + loop.Run(libuv.RUN_DEFAULT) +} + +func FreeWriteReq(req *libuv.Write) { + wr := (*WriteReq)(c.Pointer(req)) + // Free the buffer base. + if wr.Buf.Base != nil { + c.Free(c.Pointer(wr.Buf.Base)) + wr.Buf.Base = nil + } +} + +func AllocBuffer(handle *libuv.Handle, suggestedSize uintptr, buf *libuv.Buf) { + // Allocate memory for the buffer based on the suggested size. + buf.Base = (*c.Char)(c.Malloc(suggestedSize)) + buf.Len = suggestedSize +} + +func EchoWrite(req *libuv.Write, status c.Int) { + if status != 0 { + c.Fprintf(c.Stderr, c.Str("Write error: %s\n"), libuv.Strerror(libuv.Errno(status))) + } + FreeWriteReq(req) +} + +func EchoRead(client *libuv.Stream, nread c.Long, buf *libuv.Buf) { + if nread > 0 { + req := new(WriteReq) + // Initialize the buffer with the data read. + req.Buf = libuv.InitBuf(buf.Base, c.Uint(nread)) + // Write the data back to the client. + req.Req.Write(client, &req.Buf, 1, EchoWrite) + return + } + if nread < 0 { + // Handle read errors and EOF. + if (libuv.Errno)(nread) != libuv.EOF { + c.Fprintf(c.Stderr, c.Str("Read error: %s\n"), libuv.Strerror(libuv.Errno(nread))) + } + (*libuv.Handle)(c.Pointer(client)).Close(nil) + } + // Free the buffer if it's no longer needed. + if buf.Base != nil { + c.Free(c.Pointer(buf.Base)) + } +} + +func OnNewConnection(server *libuv.Stream, status c.Int) { + if status < 0 { + c.Fprintf(c.Stderr, c.Str("New connection error: %s\n"), libuv.Strerror(libuv.Errno(status))) + return + } + + // Allocate memory for a new client. + client := &libuv.Tcp{} + + if client == nil { + c.Fprintf(c.Stderr, c.Str("Failed to allocate memory for client\n")) + return + } + + // Initialize the client TCP handle. + if libuv.InitTcp(libuv.DefaultLoop(), client) < 0 { + c.Fprintf(c.Stderr, c.Str("Failed to initialize client\n")) + return + } + + // Accept the new connection and start reading data. + if server.Accept((*libuv.Stream)(client)) == 0 { + (*libuv.Stream)(client).StartRead(AllocBuffer, EchoRead) + } else { + (*libuv.Handle)(c.Pointer(client)).Close(nil) + } +} diff --git a/runtime/internal/clite/libuv/_wrap/libuv.c b/runtime/internal/clite/libuv/_wrap/libuv.c new file mode 100644 index 00000000..d3d51448 --- /dev/null +++ b/runtime/internal/clite/libuv/_wrap/libuv.c @@ -0,0 +1,5 @@ +#include + +int uv_tcp_get_io_watcher_fd (uv_tcp_t* handle) { + return handle->io_watcher.fd; +} \ No newline at end of file diff --git a/runtime/internal/clite/libuv/async.go b/runtime/internal/clite/libuv/async.go new file mode 100644 index 00000000..2b66ef16 --- /dev/null +++ b/runtime/internal/clite/libuv/async.go @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package libuv + +import ( + _ "unsafe" + + c "github.com/goplus/llgo/runtime/internal/clite" +) + +// struct uv_async_t +type Async struct { + Handle + // On macOS arm64, sizeof uv_async_t is 128 bytes. + // Handle is 92 bytes, so we need 36 bytes to fill the gap. + // Maybe reserve more for future use. + Unused [36]byte +} + +// typedef void (*uv_async_cb)(uv_async_t* handle); +// llgo:type C +type AsyncCb func(*Async) + +// int uv_async_init(uv_loop_t*, uv_async_t* async, uv_async_cb async_cb); +// +// llgo:link (*Loop).Async C.uv_async_init +func (loop *Loop) Async(a *Async, cb AsyncCb) c.Int { + return 0 +} + +// int uv_async_send(uv_async_t* async); +// +// llgo:link (*Async).Send C.uv_async_send +func (a *Async) Send() c.Int { + return 0 +} diff --git a/runtime/internal/clite/libuv/check.go b/runtime/internal/clite/libuv/check.go new file mode 100644 index 00000000..7df0fc8a --- /dev/null +++ b/runtime/internal/clite/libuv/check.go @@ -0,0 +1,27 @@ +package libuv + +import ( + _ "unsafe" + + c "github.com/goplus/llgo/runtime/internal/clite" +) + +/* Handle types. */ + +type Check struct { + Unused [120]byte +} + +/* Function type */ + +// llgo:type C +type CheckCb func(Check *Check) + +//go:linkname InitCheck C.uv_check_init +func InitCheck(loop *Loop, Check *Check) c.Int + +// llgo:link (*Check).Start C.uv_check_start +func (Check *Check) Start(CheckCb CheckCb) c.Int { return 0 } + +// llgo:link (*Check).Stop C.uv_check_stop +func (Check *Check) Stop() c.Int { return 0 } diff --git a/runtime/internal/clite/libuv/error.go b/runtime/internal/clite/libuv/error.go new file mode 100644 index 00000000..eed52891 --- /dev/null +++ b/runtime/internal/clite/libuv/error.go @@ -0,0 +1,118 @@ +package libuv + +import ( + _ "unsafe" + + c "github.com/goplus/llgo/runtime/internal/clite" + "github.com/goplus/llgo/runtime/internal/clite/net" + "github.com/goplus/llgo/runtime/internal/clite/syscall" +) + +const ( + E2BIG = Errno(syscall.E2BIG) + EACCES = Errno(syscall.EACCES) + EADDRINUSE = Errno(syscall.EADDRINUSE) + EADDRNOTAVAIL = Errno(syscall.EADDRNOTAVAIL) + EAFNOSUPPORT = Errno(syscall.EAFNOSUPPORT) + EAGAIN = Errno(syscall.EAGAIN) + EALREADY = Errno(syscall.EALREADY) + EBADF = Errno(syscall.EBADF) + EBUSY = Errno(syscall.EBUSY) + ECANCELED = Errno(syscall.ECANCELED) + ECONNABORTED = Errno(syscall.ECONNABORTED) + ECONNREFUSED = Errno(syscall.ECONNREFUSED) + ECONNRESET = Errno(syscall.ECONNRESET) + EDESTADDRREQ = Errno(syscall.EDESTADDRREQ) + EEXIST = Errno(syscall.EEXIST) + EFAULT = Errno(syscall.EFAULT) + EFBIG = Errno(syscall.EFBIG) + EHOSTUNREACH = Errno(syscall.EHOSTUNREACH) + EINTR = Errno(syscall.EINTR) + EINVAL = Errno(syscall.EINVAL) + EIO = Errno(syscall.EIO) + EISCONN = Errno(syscall.EISCONN) + EISDIR = Errno(syscall.EISDIR) + ELOOP = Errno(syscall.ELOOP) + EMFILE = Errno(syscall.EMFILE) + EMSGSIZE = Errno(syscall.EMSGSIZE) + ENAMETOOLONG = Errno(syscall.ENAMETOOLONG) + ENETDOWN = Errno(syscall.ENETDOWN) + ENETUNREACH = Errno(syscall.ENETUNREACH) + ENFILE = Errno(syscall.ENFILE) + ENOBUFS = Errno(syscall.ENOBUFS) + ENODEV = Errno(syscall.ENODEV) + ENOENT = Errno(syscall.ENOENT) + ENOMEM = Errno(syscall.ENOMEM) + ENOPROTOOPT = Errno(syscall.ENOPROTOOPT) + ENOSPC = Errno(syscall.ENOSPC) + ENOSYS = Errno(syscall.ENOSYS) + ENOTCONN = Errno(syscall.ENOTCONN) + ENOTDIR = Errno(syscall.ENOTDIR) + ENOTEMPTY = Errno(syscall.ENOTEMPTY) + ENOTSOCK = Errno(syscall.ENOTSOCK) + ENOTSUP = Errno(syscall.ENOTSUP) + EOVERFLOW = Errno(syscall.EOVERFLOW) + EPERM = Errno(syscall.EPERM) + EPIPE = Errno(syscall.EPIPE) + EPROTO = Errno(syscall.EPROTO) + EPROTONOSUPPORT = Errno(syscall.EPROTONOSUPPORT) + EPROTOTYPE = Errno(syscall.EPROTOTYPE) + ERANGE = Errno(syscall.ERANGE) + EROFS = Errno(syscall.EROFS) + ESHUTDOWN = Errno(syscall.ESHUTDOWN) + ESPIPE = Errno(syscall.ESPIPE) + ESRCH = Errno(syscall.ESRCH) + ETIMEDOUT = Errno(syscall.ETIMEDOUT) + ETXTBSY = Errno(syscall.ETXTBSY) + EXDEV = Errno(syscall.EXDEV) + ENXIO = Errno(syscall.ENXIO) + EMLINK = Errno(syscall.EMLINK) + EHOSTDOWN = Errno(syscall.EHOSTDOWN) + ENOTTY = Errno(syscall.ENOTTY) + //EFTYPE = Errno(syscall.EFTYPE) + EILSEQ = Errno(syscall.EILSEQ) + ESOCKTNOSUPPORT = Errno(syscall.ESOCKTNOSUPPORT) +) + +const ( + EAI_ADDRFAMILY = Errno(net.EAI_ADDRFAMILY) + EAI_AGAIN = Errno(net.EAI_AGAIN) + EAI_BADFLAGS = Errno(net.EAI_BADFLAGS) + EAI_BADHINTS = Errno(net.EAI_BADHINTS) + EAI_FAIL = Errno(net.EAI_FAIL) + EAI_FAMILY = Errno(net.EAI_FAMILY) + EAI_MEMORY = Errno(net.EAI_MEMORY) + EAI_NODATA = Errno(net.EAI_NODATA) + EAI_NONAME = Errno(net.EAI_NONAME) + EAI_OVERFLOW = Errno(net.EAI_OVERFLOW) + EAI_PROTOCOL = Errno(net.EAI_PROTOCOL) + EAI_SERVICE = Errno(net.EAI_SERVICE) + EAI_SOCKTYPE = Errno(net.EAI_SOCKTYPE) +) + +const ( + EAI_CANCELED Errno = -3003 + ECHARSET Errno = -4080 + ENONET Errno = -4056 + UNKNOWN Errno = -4094 + EOF Errno = -4095 + EREMOTEIO Errno = -4030 + ERRNO_MAX Errno = EOF - 1 +) + +type Errno c.Int + +//go:linkname TranslateSysError C.uv_translate_sys_error +func TranslateSysError(sysErrno c.Int) Errno + +//go:linkname Strerror C.uv_strerror +func Strerror(err Errno) *c.Char + +//go:linkname StrerrorR C.uv_strerror_r +func StrerrorR(err Errno, buf *c.Char, bufLen uintptr) *c.Char + +//go:linkname ErrName C.uv_err_name +func ErrName(err Errno) *c.Char + +//go:linkname ErrNameR C.uv_err_name_r +func ErrNameR(err Errno, buf *c.Char, bufLen uintptr) *c.Char diff --git a/runtime/internal/clite/libuv/fs.go b/runtime/internal/clite/libuv/fs.go new file mode 100644 index 00000000..4159bbee --- /dev/null +++ b/runtime/internal/clite/libuv/fs.go @@ -0,0 +1,307 @@ +package libuv + +import ( + _ "unsafe" + + c "github.com/goplus/llgo/runtime/internal/clite" +) + +const ( + FS_UNKNOWN FsType = -1 + FS_CUSTOM FsType = 0 + FS_OPEN FsType = 1 + FS_CLOSE FsType = 2 + FS_READ FsType = 3 + FS_WRITE FsType = 4 + FS_SENDFILE FsType = 5 + FS_STAT FsType = 6 + FS_LSTAT FsType = 7 + FS_FSTAT FsType = 8 + FS_FTRUNCATE FsType = 9 + FS_UTIME FsType = 10 + FS_FUTIME FsType = 11 + FS_ACCESS FsType = 12 + FS_CHMOD FsType = 13 + FS_FCHMOD FsType = 14 + FS_FSYNC FsType = 15 + FS_FDATASYNC FsType = 16 + FS_UNLINK FsType = 17 + FS_RMDIR FsType = 18 + FS_MKDIR FsType = 19 + FS_MKDTEMP FsType = 20 + FS_RENAME FsType = 21 + FS_SCANDIR FsType = 22 + FS_LINK FsType = 23 + FS_SYMLINK FsType = 24 + FS_READLINK FsType = 25 + FS_CHOWN FsType = 26 + FS_FCHOWN FsType = 27 + FS_REALPATH FsType = 28 + FS_COPYFILE FsType = 29 + FS_LCHOWN FsType = 30 + FS_OPENDIR FsType = 31 + FS_READDIR FsType = 32 + FS_CLOSEDIR FsType = 33 + FS_STATFS FsType = 34 + FS_MKSTEMP FsType = 35 + FS_LUTIME FsType = 36 +) + +const ( + DirentUnknown DirentType = iota + DirentFile + DirentDir + DirentLink + DirentFifo + DirentSocket + DirentChar + DirentBlock +) + +type FsType c.Int + +type DirentType c.Int + +type File c.Int + +// ---------------------------------------------- + +/* Handle types. */ + +type Fs struct { + Unused [440]byte +} + +type FsEvent struct { + Unused [0]byte +} + +type FsPoll struct { + Unused [0]byte +} + +type Dirent struct { + Name *c.Char + Type DirentType +} + +type Stat struct { + Unused [0]byte +} + +// ---------------------------------------------- + +/* Function type */ + +// llgo:type C +type FsCb func(req *Fs) + +// llgo:type C +type FsEventCb func(handle *FsEvent, filename *c.Char, events c.Int, status c.Int) + +// llgo:type C +type FsPollCb func(handle *FsPoll, status c.Int, events c.Int) + +// ---------------------------------------------- + +/* Fs related function and method */ + +// llgo:link (*Fs).GetType C.uv_fs_get_type +func (req *Fs) GetType() FsType { + return 0 +} + +// llgo:link (*Fs).GetPath C.uv_fs_get_path +func (req *Fs) GetPath() *c.Char { + return nil +} + +// llgo:link (*Fs).GetResult C.uv_fs_get_result +func (req *Fs) GetResult() c.Int { + return 0 +} + +// llgo:link (*Fs).GetPtr C.uv_fs_get_ptr +func (req *Fs) GetPtr() c.Pointer { + return nil +} + +// llgo:link (*Fs).GetSystemError C.uv_fs_get_system_error +func (req *Fs) GetSystemError() c.Int { + return 0 +} + +// llgo:link (*Fs).GetStatBuf C.uv_fs_get_statbuf +func (req *Fs) GetStatBuf() *Stat { + return nil +} + +// llgo:link (*Fs).ReqCleanup C.uv_fs_req_cleanup +func (req *Fs) ReqCleanup() { + // No return value needed for this method +} + +//go:linkname FsOpen C.uv_fs_open +func FsOpen(loop *Loop, req *Fs, path *c.Char, flags c.Int, mode c.Int, cb FsCb) c.Int + +//go:linkname FsClose C.uv_fs_close +func FsClose(loop *Loop, req *Fs, file File, cb FsCb) c.Int + +//go:linkname FsRead C.uv_fs_read +func FsRead(loop *Loop, req *Fs, file File, bufs *Buf, nbufs c.Uint, offset c.LongLong, cb FsCb) c.Int + +//go:linkname FsWrite C.uv_fs_write +func FsWrite(loop *Loop, req *Fs, file File, bufs *Buf, nbufs c.Uint, offset c.LongLong, cb FsCb) c.Int + +//go:linkname FsUnlink C.uv_fs_unlink +func FsUnlink(loop *Loop, req *Fs, path *c.Char, cb FsCb) c.Int + +//go:linkname FsMkdir C.uv_fs_mkdir +func FsMkdir(loop *Loop, req *Fs, path *c.Char, mode c.Int, cb FsCb) c.Int + +//go:linkname FsMkdtemp C.uv_fs_mkdtemp +func FsMkdtemp(loop *Loop, req *Fs, tpl *c.Char, cb FsCb) c.Int + +//go:linkname FsMkStemp C.uv_fs_mkstemp +func FsMkStemp(loop *Loop, req *Fs, tpl *c.Char, cb FsCb) c.Int + +//go:linkname FsRmdir C.uv_fs_rmdir +func FsRmdir(loop *Loop, req *Fs, path *c.Char, cb FsCb) c.Int + +//go:linkname FsStat C.uv_fs_stat +func FsStat(loop *Loop, req *Fs, path *c.Char, cb FsCb) c.Int + +//go:linkname FsFstat C.uv_fs_fstat +func FsFstat(loop *Loop, req *Fs, file File, cb FsCb) c.Int + +//go:linkname FsRename C.uv_fs_rename +func FsRename(loop *Loop, req *Fs, path *c.Char, newPath *c.Char, cb FsCb) c.Int + +//go:linkname FsFsync C.uv_fs_fsync +func FsFsync(loop *Loop, req *Fs, file File, cb FsCb) c.Int + +//go:linkname FsFdatasync C.uv_fs_fdatasync +func FsFdatasync(loop *Loop, req *Fs, file File, cb FsCb) c.Int + +//go:linkname FsFtruncate C.uv_fs_ftruncate +func FsFtruncate(loop *Loop, req *Fs, file File, offset c.LongLong, cb FsCb) c.Int + +//go:linkname FsSendfile C.uv_fs_sendfile +func FsSendfile(loop *Loop, req *Fs, outFd c.Int, inFd c.Int, inOffset c.LongLong, length c.Int, cb FsCb) c.Int + +//go:linkname FsAccess C.uv_fs_access +func FsAccess(loop *Loop, req *Fs, path *c.Char, flags c.Int, cb FsCb) c.Int + +//go:linkname FsChmod C.uv_fs_chmod +func FsChmod(loop *Loop, req *Fs, path *c.Char, mode c.Int, cb FsCb) c.Int + +//go:linkname FsFchmod C.uv_fs_fchmod +func FsFchmod(loop *Loop, req *Fs, file File, mode c.Int, cb FsCb) c.Int + +//go:linkname FsUtime C.uv_fs_utime +func FsUtime(loop *Loop, req *Fs, path *c.Char, atime c.Int, mtime c.Int, cb FsCb) c.Int + +//go:linkname FsFutime C.uv_fs_futime +func FsFutime(loop *Loop, req *Fs, file File, atime c.Int, mtime c.Int, cb FsCb) c.Int + +//go:linkname FsLutime C.uv_fs_lutime +func FsLutime(loop *Loop, req *Fs, path *c.Char, atime c.Int, mtime c.Int, cb FsCb) c.Int + +//go:linkname FsLink C.uv_fs_link +func FsLink(loop *Loop, req *Fs, path *c.Char, newPath *c.Char, cb FsCb) c.Int + +//go:linkname FsSymlink C.uv_fs_symlink +func FsSymlink(loop *Loop, req *Fs, path *c.Char, newPath *c.Char, flags c.Int, cb FsCb) c.Int + +//go:linkname FsReadlink C.uv_fs_read +func FsReadlink(loop *Loop, req *Fs, path *c.Char, cb FsCb) c.Int + +//go:linkname FsRealpath C.uv_fs_realpath +func FsRealpath(loop *Loop, req *Fs, path *c.Char, cb FsCb) c.Int + +//go:linkname FsCopyfile C.uv_fs_copyfile +func FsCopyfile(loop *Loop, req *Fs, path *c.Char, newPath *c.Char, flags c.Int, cb FsCb) c.Int + +//go:linkname FsScandir C.uv_fs_scandir +func FsScandir(loop *Loop, req *Fs, path *c.Char, flags c.Int, cb FsCb) c.Int + +//go:linkname FsScandirNext C.uv_fs_scandir_next +func FsScandirNext(req *Fs, ent *Dirent) c.Int + +//go:linkname FsOpenDir C.uv_fs_opendir +func FsOpenDir(loop *Loop, req *Fs, path *c.Char, cb FsCb) c.Int + +//go:linkname FsReaddir C.uv_fs_readdir +func FsReaddir(loop *Loop, req *Fs, dir c.Int, cb FsCb) c.Int + +//go:linkname FsCloseDir C.uv_fs_closedir +func FsCloseDir(loop *Loop, req *Fs) c.Int + +//go:linkname FsStatfs C.uv_fs_statfs +func FsStatfs(loop *Loop, req *Fs, path *c.Char, cb FsCb) c.Int + +//go:linkname FsChown C.uv_fs_chown +func FsChown(loop *Loop, req *Fs, path *c.Char, uid c.Int, gid c.Int, cb FsCb) c.Int + +//go:linkname FsFchown C.uv_fs_fchown +func FsFchown(loop *Loop, req *Fs, file File, uid c.Int, gid c.Int, cb FsCb) c.Int + +//go:linkname FsLchown C.uv_fs_lchown +func FsLchown(loop *Loop, req *Fs, path *c.Char, uid c.Int, gid c.Int, cb FsCb) c.Int + +//go:linkname FsLstat C.uv_fs_lstat +func FsLstat(loop *Loop, req *Fs, path *c.Char, cb FsCb) c.Int + +// ---------------------------------------------- + +/* FsEvent related function and method */ + +//go:linkname FsEventInit C.uv_fs_event_init +func FsEventInit(loop *Loop, handle *FsEvent) c.Int + +// llgo:link (*FsEvent).Start C.uv_fs_event_start +func (handle *FsEvent) Start(cb FsEventCb, path *c.Char, flags c.Int) c.Int { + return 0 +} + +// llgo:link (*FsEvent).Stop C.uv_fs_event_stop +func (handle *FsEvent) Stop() c.Int { + return 0 +} + +// llgo:link (*FsEvent).Close C.uv_fs_event_close +func (handle *FsEvent) Close() c.Int { + return 0 +} + +// llgo:link (*FsEvent).Getpath C.uv_fs_event_getpath +func (handle *FsEvent) Getpath() *c.Char { + return nil +} + +// ---------------------------------------------- + +/* FsPoll related function and method */ + +//go:linkname FsPollInit C.uv_fs_poll_init +func FsPollInit(loop *Loop, handle *FsPoll) c.Int + +// llgo:link (*FsPoll).Start C.uv_fs_poll_start +func (handle *FsPoll) Start(cb FsPollCb, path *c.Char, interval uint) c.Int { + return 0 +} + +// llgo:link (*FsPoll).Stop C.uv_fs_poll_stop +func (handle *FsPoll) Stop() c.Int { + return 0 +} + +// llgo:link (*FsPoll).Close C.uv_fs_poll_close +func (handle *FsPoll) Close() c.Int { + return 0 +} + +// llgo:link (*FsPoll).GetPath C.uv_fs_poll_getpath +func (handle *FsPoll) GetPath() *c.Char { + return nil +} diff --git a/runtime/internal/clite/libuv/idle.go b/runtime/internal/clite/libuv/idle.go new file mode 100644 index 00000000..ca6ffa7f --- /dev/null +++ b/runtime/internal/clite/libuv/idle.go @@ -0,0 +1,27 @@ +package libuv + +import ( + _ "unsafe" + + c "github.com/goplus/llgo/runtime/internal/clite" +) + +/* Handle types. */ + +type Idle struct { + Unused [120]byte +} + +/* Function type */ + +// llgo:type C +type IdleCb func(idle *Idle) + +//go:linkname InitIdle C.uv_idle_init +func InitIdle(loop *Loop, idle *Idle) c.Int + +// llgo:link (*Idle).Start C.uv_idle_start +func (idle *Idle) Start(idleCb IdleCb) c.Int { return 0 } + +// llgo:link (*Idle).Stop C.uv_idle_stop +func (idle *Idle) Stop() c.Int { return 0 } diff --git a/runtime/internal/clite/libuv/libuv.go b/runtime/internal/clite/libuv/libuv.go new file mode 100644 index 00000000..b781f1b5 --- /dev/null +++ b/runtime/internal/clite/libuv/libuv.go @@ -0,0 +1,276 @@ +package libuv + +import ( + _ "unsafe" + + c "github.com/goplus/llgo/runtime/internal/clite" + "github.com/goplus/llgo/runtime/internal/clite/net" +) + +const ( + LLGoPackage = "link: $(pkg-config --libs libuv); -luv" + LLGoFiles = "$(pkg-config --cflags libuv): _wrap/libuv.c" +) + +// ---------------------------------------------- +const ( + RUN_DEFAULT RunMode = iota + RUN_ONCE + RUN_NOWAIT +) + +const ( + LOOP_BLOCK_SIGNAL LoopOption = iota + METRICS_IDLE_TIME +) + +const ( + UV_LEAVE_GROUP Membership = iota + UV_JOIN_GROUP +) + +const ( + UNKNOWN_HANDLE HandleType = iota + ASYNC + CHECK + FS_EVENT + FS_POLL + HANDLE + IDLE + NAMED_PIPE + POLL + PREPARE + PROCESS + STREAM + TCP + TIMER + TTY + UDP + SIGNAL + FILE + HANDLE_TYPE_MAX +) + +const ( + UNKNOWN_REQ ReqType = iota + REQ + CONNECT + WRITE + SHUTDOWN + UDP_SEND + FS + WORK + GETADDRINFO + GETNAMEINFO + RANDOM + REQ_TYPE_PRIVATE + REQ_TYPE_MAX +) + +const ( + READABLE PollEvent = 1 << iota + WRITABLE + DISCONNECT + PRIPRIORITIZED +) + +type RunMode c.Int + +type LoopOption c.Int + +type Membership c.Int + +type HandleType c.Int + +type ReqType c.Int + +type OsSock c.Int + +type OsFd c.Int + +type PollEvent c.Int + +// ---------------------------------------------- + +/* Handle types. */ + +type Loop struct { + Unused [0]byte +} + +type Poll struct { + Data c.Pointer + Unused [160]byte +} + +/* Request types. */ + +type Buf struct { + Base *c.Char + Len uintptr +} // ---------------------------------------------- + +/* Function type */ + +// llgo:type C +type MallocFunc func(size uintptr) c.Pointer + +// llgo:type C +type ReallocFunc func(ptr c.Pointer, size uintptr) c.Pointer + +// llgo:type C +type CallocFunc func(count uintptr, size uintptr) c.Pointer + +// llgo:type C +type FreeFunc func(ptr c.Pointer) + +// llgo:type C +type AllocCb func(handle *Handle, suggestedSize uintptr, buf *Buf) + +// llgo:type C +type GetaddrinfoCb func(req *GetAddrInfo, status c.Int, res *net.AddrInfo) + +// llgo:type C +type GetnameinfoCb func(req *GetNameInfo, status c.Int, hostname *c.Char, service *c.Char) + +// llgo:type C +type WalkCb func(handle *Handle, arg c.Pointer) + +// llgo:type C +type PollCb func(handle *Poll, status c.Int, events c.Int) + +// ---------------------------------------------- + +//go:linkname Version C.uv_version +func Version() c.Uint + +//go:linkname VersionString C.uv_version_string +func VersionString() *c.Char + +//go:linkname LibraryShutdown C.uv_library_shutdown +func LibraryShutdown() + +//go:linkname ReplaceAllocator C.uv_replace_allocator +func ReplaceAllocator(mallocFunc MallocFunc, reallocFunc ReallocFunc, callocFunc CallocFunc, freeFunc FreeFunc) c.Int + +// ---------------------------------------------- + +/* Loop related functions and method. */ + +//go:linkname DefaultLoop C.uv_default_loop +func DefaultLoop() *Loop + +//go:linkname LoopSize C.uv_loop_size +func LoopSize() uintptr + +// llgo:link (*Loop).Run C.uv_run +func (loop *Loop) Run(mode RunMode) c.Int { + return 0 +} + +// llgo:link (*Loop).Alive C.uv_loop_alive +func (loop *Loop) Alive() c.Int { + return 0 +} + +// void uv_stop(uv_loop_t *loop) +// +// llgo:link (*Loop).Stop C.uv_stop +func (loop *Loop) Stop() {} + +// llgo:link (*Loop).Close C.uv_loop_close +func (loop *Loop) Close() c.Int { + return 0 +} + +// llgo:link (*Loop).Configure C.uv_loop_configure +func (loop *Loop) Configure(option LoopOption, arg c.Int) c.Int { + return 0 +} + +// llgo:link LoopDefault C.uv_default_loop +func LoopDefault() *Loop { + return nil +} + +// llgo:link (*Loop).Delete C.uv_loop_delete +func (loop *Loop) Delete() c.Int { + return 0 +} + +// llgo:link (*Loop).Fork C.uv_loop_fork +func (loop *Loop) Fork() c.Int { + return 0 +} + +// llgo:link (*Loop).Init C.uv_loop_init +func (loop *Loop) Init() c.Int { + return 0 +} + +// llgo:link LoopNew C.uv_loop_new +func LoopNew() *Loop { + return nil +} + +// llgo:link (*Loop).SetData C.uv_loop_set_data +func (loop *Loop) SetData(data c.Pointer) { + return +} + +// llgo:link (*Loop).GetData C.uv_loop_get_data +func (loop *Loop) GetData() c.Pointer { + return nil +} + +// llgo:link (*Loop).Now C.uv_now +func (loop *Loop) Now() c.UlongLong { + return 0 +} + +// llgo:link (*Loop).UpdateTime C.uv_update_time +func (loop *Loop) UpdateTime() { + // No return value needed for this method +} + +// llgo:link (*Loop).BackendFd C.uv_backend_fd +func (loop *Loop) BackendFd() c.Int { + return 0 +} + +// llgo:link (*Loop).BackendTimeout C.uv_backend_timeout +func (loop *Loop) BackendTimeout() c.Int { + return 0 +} + +// llgo:link (*Loop).Walk C.uv_walk +func (loop *Loop) Walk(walkCb WalkCb, arg c.Pointer) { + // No return value needed for this method +} + +// ---------------------------------------------- + +/* Buf related functions and method. */ + +//go:linkname InitBuf C.uv_buf_init +func InitBuf(base *c.Char, len c.Uint) Buf + +// ---------------------------------------------- + +/* Poll related function and method */ + +//go:linkname PollInit C.uv_poll_init +func PollInit(loop *Loop, handle *Poll, fd OsFd) c.Int + +//go:linkname PollInitSocket C.uv_poll_init_socket +func PollInitSocket(loop *Loop, handle *Poll, socket c.Int) c.Int + +// llgo:link (*Poll).Start C.uv_poll_start +func (handle *Poll) Start(events c.Int, cb PollCb) c.Int { + return 0 +} + +// llgo:link (*Poll).Stop C.uv_poll_stop +func (handle *Poll) Stop() c.Int { + return 0 +} diff --git a/runtime/internal/clite/libuv/net.go b/runtime/internal/clite/libuv/net.go new file mode 100644 index 00000000..b75ab879 --- /dev/null +++ b/runtime/internal/clite/libuv/net.go @@ -0,0 +1,543 @@ +package libuv + +import ( + _ "unsafe" + + c "github.com/goplus/llgo/runtime/internal/clite" + "github.com/goplus/llgo/runtime/internal/clite/net" +) + +const ( + /* Used with uv_tcp_bind, when an IPv6 address is used. */ + TCP_IPV6ONLY TcpFlags = 1 +) + +/* + * UDP support. + */ +const ( + /* Disables dual stack mode. */ + UDP_IPV6ONLY UdpFlags = 1 + /* + * Indicates message was truncated because read buffer was too small. The + * remainder was discarded by the OS. Used in uv_udp_recv_cb. + */ + UDP_PARTIAL UdpFlags = 2 + /* + * Indicates if SO_REUSEADDR will be set when binding the handle. + * This sets the SO_REUSEPORT socket flag on the BSDs and OS X. On other + * Unix platforms, it sets the SO_REUSEADDR flag. What that means is that + * multiple threads or processes can bind to the same address without error + * (provided they all set the flag) but only the last one to bind will receive + * any traffic, in effect "stealing" the port from the previous listener. + */ + UDP_REUSEADDR UdpFlags = 4 + /* + * Indicates that the message was received by recvmmsg, so the buffer provided + * must not be freed by the recv_cb callback. + */ + UDP_MMSG_CHUNK UdpFlags = 8 + /* + * Indicates that the buffer provided has been fully utilized by recvmmsg and + * that it should now be freed by the recv_cb callback. When this flag is set + * in uv_udp_recv_cb, nread will always be 0 and addr will always be NULL. + */ + UDP_MMSG_FREE UdpFlags = 16 + /* + * Indicates if IP_RECVERR/IPV6_RECVERR will be set when binding the handle. + * This sets IP_RECVERR for IPv4 and IPV6_RECVERR for IPv6 UDP sockets on + * Linux. This stops the Linux kernel from suppressing some ICMP error + * messages and enables full ICMP error reporting for faster failover. + * This flag is no-op on platforms other than Linux. + */ + UDP_LINUX_RECVERR UdpFlags = 32 + /* + * Indicates that recvmmsg should be used, if available. + */ + UDP_RECVMMSG UdpFlags = 256 +) + +type TcpFlags c.Int + +type UdpFlags c.Int + +// ---------------------------------------------- + +/* Handle types. */ + +// TODO(spongehah): Handle +type Handle struct { + Data c.Pointer + Unused [88]byte +} + +// TODO(spongehah): Stream +type Stream struct { + Data c.Pointer + Unused [256]byte +} + +// TODO(spongehah): Tcp +type Tcp struct { + Data c.Pointer + Unused [256]byte +} + +// TODO(spongehah): Udp +type Udp struct { + Unused [224]byte +} + +/* Request types. */ + +// TODO(spongehah): Req +type Req struct { + Unused [64]byte +} + +// TODO(spongehah): UdpSend +type UdpSend struct { + Unused [320]byte +} + +// TODO(spongehah): Write +type Write struct { + Data c.Pointer + Unused [184]byte +} + +// TODO(spongehah): Connect +type Connect struct { + Data c.Pointer + Unused [88]byte +} + +// TODO(spongehah): GetAddrInfo +type GetAddrInfo struct { + Unused [160]byte +} + +// TODO(spongehah): GetNameInfo +type GetNameInfo struct { + Unused [1320]byte +} + +// TODO(spongehah): Shutdown +type Shutdown struct { + Unused [80]byte +} + +// ---------------------------------------------- + +/* Function type */ + +// llgo:type C +type CloseCb func(handle *Handle) + +// llgo:type C +type ConnectCb func(req *Connect, status c.Int) + +// llgo:type C +type UdpSendCb func(req *UdpSend, status c.Int) + +// llgo:type C +type UdpRecvCb func(handle *Udp, nread c.Long, buf *Buf, addr *net.SockAddr, flags c.Uint) + +// llgo:type C +type ReadCb func(stream *Stream, nread c.Long, buf *Buf) + +// llgo:type C +type WriteCb func(req *Write, status c.Int) + +// llgo:type C +type ConnectionCb func(server *Stream, status c.Int) + +// llgo:type C +type ShutdownCb func(req *Shutdown, status c.Int) + +// ---------------------------------------------- + +/* Handle related function and method */ + +//go:linkname HandleSize C.uv_handle_size +func HandleSize(handleType HandleType) uintptr + +//go:linkname HandleTypeName C.uv_handle_type_name +func HandleTypeName(handleType HandleType) *c.Char + +// llgo:link (*Handle).Ref C.uv_ref +func (handle *Handle) Ref() {} + +// llgo:link (*Handle).Unref C.uv_unref +func (handle *Handle) Unref() {} + +// llgo:link (*Handle).HasRef C.uv_has_ref +func (handle *Handle) HasRef() c.Int { + return 0 +} + +// llgo:link (*Handle).GetType C.uv_handle_get_type +func (handle *Handle) GetType() HandleType { + return 0 +} + +// llgo:link (*Handle).GetData C.uv_handle_get_data +func (handle *Handle) GetData() c.Pointer { + return nil +} + +// llgo:link (*Handle).GetLoop C.uv_handle_get_loop +func (handle *Handle) GetLoop() *Loop { + return nil +} + +// llgo:link (*Handle).SetData C.uv_handle_set_data +func (handle *Handle) SetData(data c.Pointer) {} + +// llgo:link (*Handle).IsActive C.uv_is_active +func (handle *Handle) IsActive() c.Int { + return 0 +} + +// llgo:link (*Handle).Close C.uv_close +func (handle *Handle) Close(closeCb CloseCb) {} + +// llgo:link (*Handle).SendBufferSize C.uv_send_buffer_size +func (handle *Handle) SendBufferSize(value *c.Int) c.Int { + return 0 +} + +// llgo:link (*Handle).RecvBufferSize C.uv_recv_buffer_size +func (handle *Handle) RecvBufferSize(value *c.Int) c.Int { + return 0 +} + +// llgo:link (*Handle).Fileno C.uv_fileno +func (handle *Handle) Fileno(fd *OsFd) c.Int { + return 0 +} + +// llgo:link (*Handle).IsClosing C.uv_is_closing +func (handle *Handle) IsClosing() c.Int { + return 0 +} + +// llgo:link (*Handle).IsReadable C.uv_is_readable +func (handle *Handle) IsReadable() c.Int { + return 0 +} + +// llgo:link (*Handle).IsWritable C.uv_is_writable +func (handle *Handle) IsWritable() c.Int { + return 0 +} + +//go:linkname Pipe C.uv_pipe +func Pipe(fds [2]File, readFlags c.Int, writeFlags c.Int) c.Int { + return 0 +} + +//go:linkname Socketpair C.uv_socketpair +func Socketpair(_type c.Int, protocol c.Int, socketVector [2]OsSock, flag0 c.Int, flag1 c.Int) c.Int { + return 0 +} + +// ---------------------------------------------- + +/* Req related function and method */ + +//go:linkname ReqSize C.uv_req_size +func ReqSize(reqType ReqType) uintptr + +//go:linkname TypeName C.uv_req_type_name +func TypeName(reqType ReqType) *c.Char + +// llgo:link (*Req).GetData C.uv_req_get_data +func (req *Req) GetData() c.Pointer { + return nil +} + +// llgo:link (*Req).SetData C.uv_req_set_data +func (req *Req) SetData(data c.Pointer) {} + +// llgo:link (*Req).GetType C.uv_req_get_type +func (req *Req) GetType() ReqType { + return 0 +} + +// llgo:link (*Req).Cancel C.uv_cancel +func (req *Req) Cancel() c.Int { + return 0 +} + +// ---------------------------------------------- + +/* Stream related function and method */ + +// llgo:link (*Stream).GetWriteQueueSize C.uv_stream_get_write_queue_size +func (stream *Stream) GetWriteQueueSize() uintptr { + return 0 +} + +// llgo:link (*Stream).Listen C.uv_listen +func (stream *Stream) Listen(backlog c.Int, connectionCb ConnectionCb) c.Int { + return 0 +} + +// llgo:link (*Stream).Accept C.uv_accept +func (server *Stream) Accept(client *Stream) c.Int { + return 0 +} + +// llgo:link (*Stream).StartRead C.uv_read_start +func (stream *Stream) StartRead(allocCb AllocCb, readCb ReadCb) c.Int { + return 0 +} + +// llgo:link (*Stream).StopRead C.uv_read_stop +func (stream *Stream) StopRead() c.Int { + return 0 +} + +// llgo:link (*Write).Write C.uv_write +func (req *Write) Write(stream *Stream, bufs *Buf, nbufs c.Uint, writeCb WriteCb) c.Int { + return 0 +} + +// llgo:link (*Write).Write2 C.uv_write2 +func (req *Write) Write2(stream *Stream, bufs *Buf, nbufs c.Uint, sendStream *Stream, writeCb WriteCb) c.Int { + return 0 +} + +// llgo:link (*Stream).TryWrite C.uv_try_write +func (stream *Stream) TryWrite(bufs *Buf, nbufs c.Uint) c.Int { + return 0 +} + +// llgo:link (*Stream).TryWrite2 C.uv_try_write2 +func (stream *Stream) TryWrite2(bufs *Buf, nbufs c.Uint, sendStream *Stream) c.Int { + return 0 +} + +// llgo:link (*Stream).IsReadable C.uv_is_readable +func (stream *Stream) IsReadable() c.Int { + return 0 +} + +// llgo:link (*Stream).IsWritable C.uv_is_writable +func (stream *Stream) IsWritable() c.Int { + return 0 +} + +// llgo:link (*Stream).SetBlocking C.uv_stream_set_blocking +func (stream *Stream) SetBlocking(blocking c.Int) c.Int { + return 0 +} + +//go:linkname StreamShutdown C.uv_shutdown +func StreamShutdown(shutdown *Shutdown, stream *Stream, shutdownCb ShutdownCb) c.Int { + return 0 +} + +// ---------------------------------------------- + +/* Tcp related function and method */ + +//go:linkname InitTcp C.uv_tcp_init +func InitTcp(loop *Loop, tcp *Tcp) c.Int + +//go:linkname InitTcpEx C.uv_tcp_init_ex +func InitTcpEx(loop *Loop, tcp *Tcp, flags c.Uint) c.Int + +// llgo:link (*Tcp).Open C.uv_tcp_open +func (tcp *Tcp) Open(sock OsSock) c.Int { + return 0 +} + +// llgo:link (*Tcp).Nodelay C.uv_tcp_nodelay +func (tcp *Tcp) Nodelay(enable c.Int) c.Int { + return 0 +} + +// llgo:link (*Tcp).KeepAlive C.uv_tcp_keepalive +func (tcp *Tcp) KeepAlive(enable c.Int, delay c.Uint) c.Int { + return 0 +} + +// llgo:link (*Tcp).SimultaneousAccepts C.uv_tcp_simultaneous_accepts +func (tcp *Tcp) SimultaneousAccepts(enable c.Int) c.Int { + return 0 +} + +// llgo:link (*Tcp).Bind C.uv_tcp_bind +func (tcp *Tcp) Bind(addr *net.SockAddr, flags c.Uint) c.Int { + return 0 +} + +// llgo:link (*Tcp).Getsockname C.uv_tcp_getsockname +func (tcp *Tcp) Getsockname(name *net.SockAddr, nameLen *c.Int) c.Int { + return 0 +} + +// llgo:link (*Tcp).Getpeername C.uv_tcp_getpeername +func (tcp *Tcp) Getpeername(name *net.SockAddr, nameLen *c.Int) c.Int { + return 0 +} + +// llgo:link (*Tcp).CloseReset C.uv_tcp_close_reset +func (tcp *Tcp) CloseReset(closeCb CloseCb) c.Int { + return 0 +} + +// llgo:link (*Tcp).GetIoWatcherFd C.uv_tcp_get_io_watcher_fd +func (tcp *Tcp) GetIoWatcherFd() c.Int { + return 0 +} + +//go:linkname TcpConnect C.uv_tcp_connect +func TcpConnect(req *Connect, tcp *Tcp, addr *net.SockAddr, connectCb ConnectCb) c.Int + +// ---------------------------------------------- + +/* Udp related function and method */ + +//go:linkname InitUdp C.uv_udp_init +func InitUdp(loop *Loop, udp *Udp) c.Int + +//go:linkname InitUdpEx C.uv_udp_init_ex +func InitUdpEx(loop *Loop, udp *Udp, flags c.Uint) c.Int + +// llgo:link (*Udp).Open C.uv_udp_open +func (udp *Udp) Open(sock OsSock) c.Int { + return 0 +} + +// llgo:link (*Udp).Bind C.uv_udp_bind +func (udp *Udp) Bind(addr *net.SockAddr, flags c.Uint) c.Int { + return 0 +} + +// llgo:link (*Udp).Connect C.uv_udp_connect +func (udp *Udp) Connect(addr *net.SockAddr) c.Int { + return 0 +} + +// llgo:link (*Udp).Getpeername C.uv_udp_getpeername +func (udp *Udp) Getpeername(name *net.SockAddr, nameLen *c.Int) c.Int { + return 0 +} + +// llgo:link (*Udp).Getsockname C.uv_udp_getsockname +func (udp *Udp) Getsockname(name *net.SockAddr, nameLen *c.Int) c.Int { + return 0 +} + +// llgo:link (*Udp).SetMembership C.uv_udp_set_membership +func (udp *Udp) SetMembership(multicastAddr *c.Char, interfaceAddr *c.Char, membership Membership) c.Int { + return 0 +} + +// llgo:link (*Udp).SourceMembership C.uv_udp_set_source_membership +func (udp *Udp) SourceMembership(multicastAddr *c.Char, interfaceAddr *c.Char, sourceAddr *c.Char, membership Membership) c.Int { + return 0 +} + +// llgo:link (*Udp).SetMulticastLoop C.uv_udp_set_multicast_loop +func (udp *Udp) SetMulticastLoop(on c.Int) c.Int { + return 0 +} + +// llgo:link (*Udp).SetMulticastTTL C.uv_udp_set_multicast_ttl +func (udp *Udp) SetMulticastTTL(ttl c.Int) c.Int { + return 0 +} + +// llgo:link (*Udp).SetMulticastInterface C.uv_udp_set_multicast_interface +func (udp *Udp) SetMulticastInterface(interfaceAddr *c.Char) c.Int { + return 0 +} + +// llgo:link (*Udp).SetBroadcast C.uv_udp_set_broadcast +func (udp *Udp) SetBroadcast(on c.Int) c.Int { + return 0 +} + +// llgo:link (*Udp).SetTTL C.uv_udp_set_ttl +func (udp *Udp) SetTTL(ttl c.Int) c.Int { + return 0 +} + +// llgo:link (*Udp).TrySend C.uv_udp_try_send +func (udp *Udp) TrySend(bufs *Buf, nbufs c.Uint, addr *net.SockAddr) c.Int { + return 0 +} + +// llgo:link (*Udp).StartRecv C.uv_udp_recv_start +func (udp *Udp) StartRecv(allocCb AllocCb, recvCb UdpRecvCb) c.Int { + return 0 +} + +// llgo:link (*Udp).UsingRecvmmsg C.uv_udp_using_recvmmsg +func (udp *Udp) UsingRecvmmsg() c.Int { + return 0 +} + +// llgo:link (*Udp).StopRecv C.uv_udp_recv_stop +func (udp *Udp) StopRecv() c.Int { + return 0 +} + +// llgo:link (*Udp).GetSendQueueSize C.uv_udp_get_send_queue_size +func (udp *Udp) GetSendQueueSize() uintptr { + return 0 +} + +// llgo:link (*Udp).GetSendQueueCount C.uv_udp_get_send_queue_count +func (udp *Udp) GetSendQueueCount() uintptr { + return 0 +} + +//go:linkname Send C.uv_udp_send +func Send(req *UdpSend, udp *Udp, bufs *Buf, nbufs c.Uint, addr *net.SockAddr, sendCb UdpSendCb) c.Int + +// ---------------------------------------------- + +/* DNS related function and method */ + +//go:linkname Ip4Addr C.uv_ip4_addr +func Ip4Addr(ip *c.Char, port c.Int, addr *net.SockaddrIn) c.Int + +//go:linkname Ip6Addr C.uv_ip6_addr +func Ip6Addr(ip *c.Char, port c.Int, addr *net.SockaddrIn6) c.Int + +//go:linkname Ip4Name C.uv_ip4_name +func Ip4Name(src *net.SockaddrIn, dst *c.Char, size uintptr) c.Int + +//go:linkname Ip6Name C.uv_ip6_name +func Ip6Name(src *net.SockaddrIn6, dst *c.Char, size uintptr) c.Int + +//go:linkname IpName C.uv_ip_name +func IpName(src *net.SockAddr, dst *c.Char, size uintptr) c.Int + +//go:linkname InetNtop C.uv_inet_ntop +func InetNtop(af c.Int, src c.Pointer, dst *c.Char, size uintptr) c.Int + +//go:linkname InetPton C.uv_inet_pton +func InetPton(af c.Int, src *c.Char, dst c.Pointer) c.Int + +// ---------------------------------------------- + +/* Getaddrinfo related function and method */ + +//go:linkname Getaddrinfo C.uv_getaddrinfo +func Getaddrinfo(loop *Loop, req *GetAddrInfo, getaddrinfoCb GetaddrinfoCb, node *c.Char, service *c.Char, hints *net.AddrInfo) c.Int + +//go:linkname Freeaddrinfo C.uv_freeaddrinfo +func Freeaddrinfo(addrInfo *net.AddrInfo) + +// ---------------------------------------------- + +/* Getnameinfo related function and method */ + +//go:linkname Getnameinfo C.uv_getnameinfo +func Getnameinfo(loop *Loop, req *GetNameInfo, getnameinfoCb GetnameinfoCb, addr *net.SockAddr, flags c.Int) c.Int diff --git a/runtime/internal/clite/libuv/signal.go b/runtime/internal/clite/libuv/signal.go new file mode 100644 index 00000000..549f1c4b --- /dev/null +++ b/runtime/internal/clite/libuv/signal.go @@ -0,0 +1,42 @@ +package libuv + +import ( + _ "unsafe" + + c "github.com/goplus/llgo/runtime/internal/clite" +) + +/* Handle types. */ + +type Signal struct { + Unused [152]byte +} + +// ---------------------------------------------- + +/* Function type */ + +// llgo:type C +type SignalCb func(handle *Signal, sigNum c.Int) + +// ---------------------------------------------- + +/* Signal related functions and method. */ + +//go:linkname SignalInit C.uv_signal_init +func SignalInit(loop *Loop, handle *Signal) c.Int + +// llgo:link (*Signal).Start C.uv_signal_start +func (handle *Signal) Start(cb SignalCb, signum c.Int) c.Int { + return 0 +} + +// llgo:link (*Signal).StartOneshot C.uv_signal_start_oneshot +func (handle *Signal) StartOneshot(cb SignalCb, signum c.Int) c.Int { + return 0 +} + +// llgo:link (*Signal).Stop C.uv_signal_stop +func (handle *Signal) Stop() c.Int { + return 0 +} diff --git a/runtime/internal/clite/libuv/thread.go b/runtime/internal/clite/libuv/thread.go new file mode 100644 index 00000000..7f472813 --- /dev/null +++ b/runtime/internal/clite/libuv/thread.go @@ -0,0 +1,78 @@ +package libuv + +import ( + _ "unsafe" + + c "github.com/goplus/llgo/runtime/internal/clite" +) + +type Thread struct { + Unused [8]byte +} + +type ThreadOptions struct { + flags c.Uint + stackSize uintptr +} + +type Work struct { + Unused [128]byte +} + +// ---------------------------------------------- + +/* Function type */ + +// llgo:type C +type ThreadCb func(arg c.Pointer) + +//llgo:type C +type WorkCb func(req *Work) + +//llgo:type C +type AfterWorkCb func(req *Work, status c.Int) + +// ---------------------------------------------- + +/* Thread related functions and method. */ + +//go:linkname ThreadEqual C.uv_thread_equal +func ThreadEqual(t1 *Thread, t2 *Thread) c.Int + +//go:linkname ThreadGetCPU C.uv_thread_getcpu +func ThreadGetCPU() c.Int + +//go:linkname ThreadSelf C.uv_thread_self +func ThreadSelf() Thread + +// llgo:link (*Thread).Create C.uv_thread_create +func (t *Thread) Create(entry ThreadCb, arg c.Pointer) c.Int { + return 0 +} + +// llgo:link (*Thread).CreateEx C.uv_thread_create_ex +func (t *Thread) CreateEx(entry ThreadCb, params *ThreadOptions, arg c.Pointer) c.Int { + return 0 +} + +// llgo:link (*Thread).Join C.uv_thread_join +func (t *Thread) Join() c.Int { + return 0 +} + +// llgo:link (*Thread).SetAffinity C.uv_thread_set_affinity +func (t *Thread) SetAffinity(cpuMask *c.Char, oldMask *c.Char, maskSize uintptr) c.Int { + return 0 +} + +// llgo:link (*Thread).GetAffinity C.uv_thread_get_affinity +func (t *Thread) GetAffinity(cpuMask *c.Char, maskSize uintptr) c.Int { + return 0 +} + +// ---------------------------------------------- + +/* Work related functions and method. */ + +//go:linkname QueueWork C.uv_queue_work +func QueueWork(loop *Loop, req *Work, workCb WorkCb, afterWorkCb AfterWorkCb) c.Int diff --git a/runtime/internal/clite/libuv/timer.go b/runtime/internal/clite/libuv/timer.go new file mode 100644 index 00000000..658c9422 --- /dev/null +++ b/runtime/internal/clite/libuv/timer.go @@ -0,0 +1,56 @@ +package libuv + +import ( + _ "unsafe" + + c "github.com/goplus/llgo/runtime/internal/clite" +) + +// ---------------------------------------------- + +/* Handle types. */ + +// TODO(spongehah): Timer +type Timer struct { + Unused [152]byte +} + +// ---------------------------------------------- + +// llgo:type C +type TimerCb func(timer *Timer) + +// ---------------------------------------------- + +/* Timer related function and method */ + +//go:linkname InitTimer C.uv_timer_init +func InitTimer(loop *Loop, timer *Timer) c.Int + +// llgo:link (*Timer).Start C.uv_timer_start +func (timer *Timer) Start(cb TimerCb, timeoutMs uint64, repeat uint64) c.Int { + return 0 +} + +// llgo:link (*Timer).Stop C.uv_timer_stop +func (timer *Timer) Stop() c.Int { + return 0 +} + +// llgo:link (*Timer).Again C.uv_timer_again +func (timer *Timer) Again() c.Int { + return 0 +} + +// llgo:link (*Timer).SetRepeat C.uv_timer_set_repeat +func (timer *Timer) SetRepeat(repeat uint64) {} + +// llgo:link (*Timer).GetRepeat C.uv_timer_get_repeat +func (timer *Timer) GetRepeat() uint64 { + return 0 +} + +// llgo:link (*Timer).GetDueIn C.uv_timer_get_due_in +func (timer *Timer) GetDueIn() uint64 { + return 0 +} diff --git a/runtime/internal/clite/net/net.go b/runtime/internal/clite/net/net.go new file mode 100644 index 00000000..35d43bba --- /dev/null +++ b/runtime/internal/clite/net/net.go @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net + +import ( + "unsafe" + + c "github.com/goplus/llgo/runtime/internal/clite" +) + +const ( + LLGoPackage = true +) + +const ( + AF_UNSPEC = 0 // unspecified + AF_UNIX = 1 // local to host (pipes) + AF_LOCAL = AF_UNIX // backward compatibility + AF_INET = 2 // internetwork: UDP, TCP, etc. + AF_IMPLINK = 3 // arpanet imp addresses + AF_PUP = 4 // pup protocols: e.g. BSP + AF_CHAOS = 5 // mit CHAOS protocols + AF_NS = 6 // XEROX NS protocols + AF_ISO = 7 // ISO protocols + AF_OSI = AF_ISO + AF_ECMA = 8 // European computer manufacturers + AF_DATAKIT = 9 // datakit protocols + AF_CCITT = 10 // CCITT protocols, X.25 etc + AF_SNA = 11 // IBM SNA + AF_DECnet = 12 // DECnet + AF_DLI = 13 // DEC Direct data link interface + AF_LAT = 14 // LAT + AF_HYLINK = 15 // NSC Hyperchannel + AF_APPLETALK = 16 // Apple Talk + AF_ROUTE = 17 // Internal Routing Protocol + AF_LINK = 18 // Link layer interface + pseudo_AF_XTP = 19 // eXpress Transfer Protocol (no AF) + AF_COIP = 20 // connection-oriented IP, aka ST II + AF_CNT = 21 // Computer Network Technology + pseudo_AF_RTIP = 22 // Help Identify RTIP packets + AF_IPX = 23 // Novell Internet Protocol + AF_SIP = 24 // Simple Internet Protocol + pseudo_AF_PIP = 25 // Help Identify PIP packets + AF_NDRV = 27 // Network Driver 'raw' access + AF_ISDN = 28 // Integrated Services Digital Network + AF_E164 = AF_ISDN // CCITT E.164 recommendation + pseudo_AF_KEY = 29 // Internal key-management function + AF_INET6 = 30 // IPv6 + AF_NATM = 31 // native ATM access + AF_SYSTEM = 32 // Kernel event messages + AF_NETBIOS = 33 // NetBIOS + AF_PPP = 34 // PPP communication protocol + pseudo_AF_HDRCMPLT = 35 // Used by BPF to not rewrite headers in interface output routine + AF_RESERVED_36 = 36 // Reserved for internal usage + AF_IEEE80211 = 37 // IEEE 802.11 protocol + AF_UTUN = 38 + AF_VSOCK = 40 // VM Sockets + AF_MAX = 41 +) + +const ( + SOCK_STREAM = 1 // stream socket + SOCK_DGRAM = 2 // datagram socket + SOCK_RAW = 3 // raw-protocol interface + SOCK_RDM = 4 // reliably-delivered message + SOCK_SEQPACKET = 5 // sequenced packet stream +) + +const ( + EAI_ADDRFAMILY = iota + 1 /* address family for hostname not supported */ + EAI_AGAIN /* temporary failure in name resolution */ + EAI_BADFLAGS /* invalid value for ai_flags */ + EAI_FAIL /* non-recoverable failure in name resolution */ + EAI_FAMILY /* ai_family not supported */ + EAI_MEMORY /* memory allocation failure */ + EAI_NODATA /* no address associated with hostname */ + EAI_NONAME /* hostname nor servname provided, or not known */ + EAI_SERVICE /* servname not supported for ai_socktype */ + EAI_SOCKTYPE /* ai_socktype not supported */ + EAI_SYSTEM /* system error returned in errno */ + EAI_BADHINTS /* invalid value for hints */ + EAI_PROTOCOL /* resolved protocol is unknown */ + EAI_OVERFLOW /* argument buffer overflow */ +) + +const ( + ALIGNSIZE = unsafe.Sizeof(c.LongLong(0)) + MAXSIZE = 128 + PAD1_SIZE = ALIGNSIZE - unsafe.Sizeof(byte(0)) - unsafe.Sizeof(byte(0)) + PAD2_SIZE = MAXSIZE - unsafe.Sizeof(byte(0)) - unsafe.Sizeof(byte(0)) - PAD1_SIZE - ALIGNSIZE +) + +// (TODO) merge to inet +const INET_ADDRSTRLEN = 16 + +type SockaddrIn struct { + Len uint8 + Family uint8 + Port uint16 + Addr InAddr + Zero [8]c.Char +} + +type SockaddrIn6 struct { + Len uint8 + Family uint8 + Port uint16 + Flowinfo c.Uint + Addr In6Addr + ScopeId c.Uint +} + +type SockaddrStorage struct { + Len uint8 + Family uint8 + pad1 [PAD1_SIZE]c.Char + align c.LongLong + pad2 [PAD2_SIZE]c.Char +} + +type InAddr struct { + Addr c.Uint +} + +type In6Addr struct { + U6Addr [16]uint8 +} + +type SockAddr struct { + Len uint8 + Family uint8 + Data [14]c.Char +} + +type Hostent struct { + Name *c.Char // official name of host + Aliases **c.Char // null-terminated array of alternate names for the host + AddrType c.Int // host address type + Length c.Int // length of address + AddrList **c.Char // null-terminated array of addresses for the host +} + +//go:linkname Socket C.socket +func Socket(domain c.Int, typ c.Int, protocol c.Int) c.Int + +//go:linkname Bind C.bind +func Bind(sockfd c.Int, addr *SockaddrIn, addrlen c.Uint) c.Int + +//go:linkname Connect C.connect +func Connect(sockfd c.Int, addr *SockAddr, addrlen c.Uint) c.Int + +//go:linkname Listen C.listen +func Listen(sockfd c.Int, backlog c.Int) c.Int + +//go:linkname Accept C.accept +func Accept(sockfd c.Int, addr *SockaddrIn, addrlen *c.Uint) c.Int + +//go:linkname GetHostByName C.gethostbyname +func GetHostByName(name *c.Char) *Hostent + +// (TODO) merge to inet +// +//go:linkname InetNtop C.inet_ntop +func InetNtop(af c.Int, src c.Pointer, dst *c.Char, size c.Uint) *c.Char + +//go:linkname InetAddr C.inet_addr +func InetAddr(s *c.Char) c.Uint + +//go:linkname Send C.send +func Send(c.Int, c.Pointer, uintptr, c.Int) c.Long + +//go:linkname Recv C.recv +func Recv(c.Int, c.Pointer, uintptr, c.Int) c.Long + +//go:linkname SetSockOpt C.setsockopt +func SetSockOpt(socket c.Int, level c.Int, optionName c.Int, optionValue c.Pointer, sockLen c.Uint) c.Int + +//go:linkname Ntohs C.ntohs +func Ntohs(x uint16) uint16 + +//go:linkname Htons C.htons +func Htons(x uint16) uint16 + +//go:linkname Ntohl C.ntohl +func Ntohl(x c.Uint) c.Uint + +//go:linkname Htonl C.htonl +func Htonl(x c.Uint) c.Uint + +// ----------------------------------------------------------------------------- + +type AddrInfo struct { + Flags c.Int + Family c.Int + SockType c.Int + Protocol c.Int + AddrLen c.Uint + CanOnName *c.Char + Addr *SockAddr + Next *AddrInfo +} + +//go:linkname Getaddrinfo C.getaddrinfo +func Getaddrinfo(host *c.Char, port *c.Char, addrInfo *AddrInfo, result **AddrInfo) c.Int + +//go:linkname Freeaddrinfo C.freeaddrinfo +func Freeaddrinfo(addrInfo *AddrInfo) c.Int + +// ----------------------------------------------------------------------------- diff --git a/runtime/internal/clite/os/os.go b/runtime/internal/clite/os/os.go index c41c97c2..c87c6590 100644 --- a/runtime/internal/clite/os/os.go +++ b/runtime/internal/clite/os/os.go @@ -94,6 +94,9 @@ func Readlink(path *c.Char, buf c.Pointer, bufsize uintptr) int //go:linkname Unlink C.unlink func Unlink(path *c.Char) c.Int +//go:linkname Unlinkat C.unlinkat +func Unlinkat(dirfd c.Int, path *c.Char, flags c.Int) c.Int + //go:linkname Remove C.remove func Remove(path *c.Char) c.Int diff --git a/runtime/internal/clite/syscall/syscall.go b/runtime/internal/clite/syscall/syscall.go index e9a08e06..0f1e61ae 100644 --- a/runtime/internal/clite/syscall/syscall.go +++ b/runtime/internal/clite/syscall/syscall.go @@ -20,7 +20,7 @@ const ( LLGoPackage = "noinit" ) -type Errno = uintptr +type Errno uintptr // A Signal is a number describing a process signal. // It implements the os.Signal interface. @@ -30,3 +30,7 @@ type Signal = int func (ts *Timespec) Unix() (sec int64, nsec int64) { return int64(ts.Sec), int64(ts.Nsec) } + +func (iov *Iovec) SetLen(length int) { + iov.Len = uint64(length) +} diff --git a/runtime/internal/lib/crypto/hmac/hmac.go b/runtime/internal/lib/crypto/hmac/hmac.go index 99633d3e..825d1093 100644 --- a/runtime/internal/lib/crypto/hmac/hmac.go +++ b/runtime/internal/lib/crypto/hmac/hmac.go @@ -1,6 +1,5 @@ package hmac -// llgo:skipall import ( "crypto/sha256" "crypto/subtle" @@ -11,6 +10,9 @@ import ( "github.com/goplus/llgo/runtime/internal/clite/openssl" ) +// llgo:skipall +type _hmac struct{} + type eface struct { _type unsafe.Pointer funcPtr *unsafe.Pointer diff --git a/runtime/internal/lib/crypto/md5/md5.go b/runtime/internal/lib/crypto/md5/md5.go index 76eb531a..dc270166 100644 --- a/runtime/internal/lib/crypto/md5/md5.go +++ b/runtime/internal/lib/crypto/md5/md5.go @@ -16,7 +16,6 @@ package md5 -// llgo:skipall import ( "crypto" "hash" @@ -26,6 +25,9 @@ import ( "github.com/goplus/llgo/runtime/internal/clite/openssl" ) +// llgo:skipall +type _md5 struct{} + func init() { crypto.RegisterHash(crypto.MD5, New) } diff --git a/runtime/internal/lib/crypto/rand/rand.go b/runtime/internal/lib/crypto/rand/rand.go index 599f5a6c..2c991960 100644 --- a/runtime/internal/lib/crypto/rand/rand.go +++ b/runtime/internal/lib/crypto/rand/rand.go @@ -16,7 +16,6 @@ package rand -// llgo:skipall import ( "io" @@ -25,6 +24,9 @@ import ( "github.com/qiniu/x/errors" ) +// llgo:skipall +type _rand struct{} + type rndReader struct{} func (rndReader) Read(p []byte) (n int, err error) { diff --git a/runtime/internal/lib/crypto/sha1/sha1.go b/runtime/internal/lib/crypto/sha1/sha1.go index 79c1c505..035384b5 100644 --- a/runtime/internal/lib/crypto/sha1/sha1.go +++ b/runtime/internal/lib/crypto/sha1/sha1.go @@ -16,7 +16,6 @@ package sha1 -// llgo:skipall import ( "crypto" "hash" @@ -26,6 +25,9 @@ import ( "github.com/goplus/llgo/runtime/internal/clite/openssl" ) +// llgo:skipall +type _sha1 struct{} + func init() { crypto.RegisterHash(crypto.SHA1, New) } diff --git a/runtime/internal/lib/crypto/sha256/sha256.go b/runtime/internal/lib/crypto/sha256/sha256.go index cce5bcc2..289875e0 100644 --- a/runtime/internal/lib/crypto/sha256/sha256.go +++ b/runtime/internal/lib/crypto/sha256/sha256.go @@ -16,7 +16,6 @@ package sha256 -// llgo:skipall import ( "crypto" "hash" @@ -26,6 +25,9 @@ import ( "github.com/goplus/llgo/runtime/internal/clite/openssl" ) +// llgo:skipall +type _sha256 struct{} + func init() { crypto.RegisterHash(crypto.SHA224, New224) crypto.RegisterHash(crypto.SHA256, New) diff --git a/runtime/internal/lib/crypto/sha512/sha512.go b/runtime/internal/lib/crypto/sha512/sha512.go index adff894e..f2a3e028 100644 --- a/runtime/internal/lib/crypto/sha512/sha512.go +++ b/runtime/internal/lib/crypto/sha512/sha512.go @@ -16,7 +16,6 @@ package sha512 -// llgo:skipall import ( "crypto" "hash" @@ -26,6 +25,9 @@ import ( "github.com/goplus/llgo/runtime/internal/clite/openssl" ) +// llgo:skipall +type _sha512 struct{} + func init() { crypto.RegisterHash(crypto.SHA384, New384) crypto.RegisterHash(crypto.SHA512, New) diff --git a/runtime/internal/lib/fmt/errors.go b/runtime/internal/lib/fmt/errors.go deleted file mode 100644 index 1fbd39f8..00000000 --- a/runtime/internal/lib/fmt/errors.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package fmt - -import ( - "errors" - "sort" -) - -// Errorf formats according to a format specifier and returns the string as a -// value that satisfies error. -// -// If the format specifier includes a %w verb with an error operand, -// the returned error will implement an Unwrap method returning the operand. -// If there is more than one %w verb, the returned error will implement an -// Unwrap method returning a []error containing all the %w operands in the -// order they appear in the arguments. -// It is invalid to supply the %w verb with an operand that does not implement -// the error interface. The %w verb is otherwise a synonym for %v. -func Errorf(format string, a ...any) error { - p := newPrinter() - p.wrapErrs = true - p.doPrintf(format, a) - s := string(p.buf) - var err error - switch len(p.wrappedErrs) { - case 0: - err = errors.New(s) - case 1: - w := &wrapError{msg: s} - w.err, _ = a[p.wrappedErrs[0]].(error) - err = w - default: - if p.reordered { - sort.Ints(p.wrappedErrs) - } - var errs []error - for i, argNum := range p.wrappedErrs { - if i > 0 && p.wrappedErrs[i-1] == argNum { - continue - } - if e, ok := a[argNum].(error); ok { - errs = append(errs, e) - } - } - err = &wrapErrors{s, errs} - } - p.free() - return err -} - -type wrapError struct { - msg string - err error -} - -func (e *wrapError) Error() string { - return e.msg -} - -func (e *wrapError) Unwrap() error { - return e.err -} - -type wrapErrors struct { - msg string - errs []error -} - -func (e *wrapErrors) Error() string { - return e.msg -} - -func (e *wrapErrors) Unwrap() []error { - return e.errs -} diff --git a/runtime/internal/lib/fmt/fmt.go b/runtime/internal/lib/fmt/fmt.go deleted file mode 100644 index 6d97abca..00000000 --- a/runtime/internal/lib/fmt/fmt.go +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package fmt - -import ( - _ "unsafe" -) - -// // llgo:skipall -type _fmt struct{} diff --git a/runtime/internal/lib/fmt/format.go b/runtime/internal/lib/fmt/format.go deleted file mode 100644 index 617f78f1..00000000 --- a/runtime/internal/lib/fmt/format.go +++ /dev/null @@ -1,594 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package fmt - -import ( - "strconv" - "unicode/utf8" -) - -const ( - ldigits = "0123456789abcdefx" - udigits = "0123456789ABCDEFX" -) - -const ( - signed = true - unsigned = false -) - -// flags placed in a separate struct for easy clearing. -type fmtFlags struct { - widPresent bool - precPresent bool - minus bool - plus bool - sharp bool - space bool - zero bool - - // For the formats %+v %#v, we set the plusV/sharpV flags - // and clear the plus/sharp flags since %+v and %#v are in effect - // different, flagless formats set at the top level. - plusV bool - sharpV bool -} - -// A fmt is the raw formatter used by Printf etc. -// It prints into a buffer that must be set up separately. -type fmt struct { - buf *buffer - - fmtFlags - - wid int // width - prec int // precision - - // intbuf is large enough to store %b of an int64 with a sign and - // avoids padding at the end of the struct on 32 bit architectures. - intbuf [68]byte -} - -func (f *fmt) clearflags() { - f.fmtFlags = fmtFlags{} -} - -func (f *fmt) init(buf *buffer) { - f.buf = buf - f.clearflags() -} - -// writePadding generates n bytes of padding. -func (f *fmt) writePadding(n int) { - if n <= 0 { // No padding bytes needed. - return - } - buf := *f.buf - oldLen := len(buf) - newLen := oldLen + n - // Make enough room for padding. - if newLen > cap(buf) { - buf = make(buffer, cap(buf)*2+n) - copy(buf, *f.buf) - } - // Decide which byte the padding should be filled with. - padByte := byte(' ') - if f.zero { - padByte = byte('0') - } - // Fill padding with padByte. - padding := buf[oldLen:newLen] - for i := range padding { - padding[i] = padByte - } - *f.buf = buf[:newLen] -} - -// pad appends b to f.buf, padded on left (!f.minus) or right (f.minus). -func (f *fmt) pad(b []byte) { - if !f.widPresent || f.wid == 0 { - f.buf.write(b) - return - } - width := f.wid - utf8.RuneCount(b) - if !f.minus { - // left padding - f.writePadding(width) - f.buf.write(b) - } else { - // right padding - f.buf.write(b) - f.writePadding(width) - } -} - -// padString appends s to f.buf, padded on left (!f.minus) or right (f.minus). -func (f *fmt) padString(s string) { - if !f.widPresent || f.wid == 0 { - f.buf.writeString(s) - return - } - width := f.wid - utf8.RuneCountInString(s) - if !f.minus { - // left padding - f.writePadding(width) - f.buf.writeString(s) - } else { - // right padding - f.buf.writeString(s) - f.writePadding(width) - } -} - -// fmtBoolean formats a boolean. -func (f *fmt) fmtBoolean(v bool) { - if v { - f.padString("true") - } else { - f.padString("false") - } -} - -// fmtUnicode formats a uint64 as "U+0078" or with f.sharp set as "U+0078 'x'". -func (f *fmt) fmtUnicode(u uint64) { - buf := f.intbuf[0:] - - // With default precision set the maximum needed buf length is 18 - // for formatting -1 with %#U ("U+FFFFFFFFFFFFFFFF") which fits - // into the already allocated intbuf with a capacity of 68 bytes. - prec := 4 - if f.precPresent && f.prec > 4 { - prec = f.prec - // Compute space needed for "U+" , number, " '", character, "'". - width := 2 + prec + 2 + utf8.UTFMax + 1 - if width > len(buf) { - buf = make([]byte, width) - } - } - - // Format into buf, ending at buf[i]. Formatting numbers is easier right-to-left. - i := len(buf) - - // For %#U we want to add a space and a quoted character at the end of the buffer. - if f.sharp && u <= utf8.MaxRune && strconv.IsPrint(rune(u)) { - i-- - buf[i] = '\'' - i -= utf8.RuneLen(rune(u)) - utf8.EncodeRune(buf[i:], rune(u)) - i-- - buf[i] = '\'' - i-- - buf[i] = ' ' - } - // Format the Unicode code point u as a hexadecimal number. - for u >= 16 { - i-- - buf[i] = udigits[u&0xF] - prec-- - u >>= 4 - } - i-- - buf[i] = udigits[u] - prec-- - // Add zeros in front of the number until requested precision is reached. - for prec > 0 { - i-- - buf[i] = '0' - prec-- - } - // Add a leading "U+". - i-- - buf[i] = '+' - i-- - buf[i] = 'U' - - oldZero := f.zero - f.zero = false - f.pad(buf[i:]) - f.zero = oldZero -} - -// fmtInteger formats signed and unsigned integers. -func (f *fmt) fmtInteger(u uint64, base int, isSigned bool, verb rune, digits string) { - negative := isSigned && int64(u) < 0 - if negative { - u = -u - } - - buf := f.intbuf[0:] - // The already allocated f.intbuf with a capacity of 68 bytes - // is large enough for integer formatting when no precision or width is set. - if f.widPresent || f.precPresent { - // Account 3 extra bytes for possible addition of a sign and "0x". - width := 3 + f.wid + f.prec // wid and prec are always positive. - if width > len(buf) { - // We're going to need a bigger boat. - buf = make([]byte, width) - } - } - - // Two ways to ask for extra leading zero digits: %.3d or %03d. - // If both are specified the f.zero flag is ignored and - // padding with spaces is used instead. - prec := 0 - if f.precPresent { - prec = f.prec - // Precision of 0 and value of 0 means "print nothing" but padding. - if prec == 0 && u == 0 { - oldZero := f.zero - f.zero = false - f.writePadding(f.wid) - f.zero = oldZero - return - } - } else if f.zero && f.widPresent { - prec = f.wid - if negative || f.plus || f.space { - prec-- // leave room for sign - } - } - - // Because printing is easier right-to-left: format u into buf, ending at buf[i]. - // We could make things marginally faster by splitting the 32-bit case out - // into a separate block but it's not worth the duplication, so u has 64 bits. - i := len(buf) - // Use constants for the division and modulo for more efficient code. - // Switch cases ordered by popularity. - switch base { - case 10: - for u >= 10 { - i-- - next := u / 10 - buf[i] = byte('0' + u - next*10) - u = next - } - case 16: - for u >= 16 { - i-- - buf[i] = digits[u&0xF] - u >>= 4 - } - case 8: - for u >= 8 { - i-- - buf[i] = byte('0' + u&7) - u >>= 3 - } - case 2: - for u >= 2 { - i-- - buf[i] = byte('0' + u&1) - u >>= 1 - } - default: - panic("fmt: unknown base; can't happen") - } - i-- - buf[i] = digits[u] - for i > 0 && prec > len(buf)-i { - i-- - buf[i] = '0' - } - - // Various prefixes: 0x, -, etc. - if f.sharp { - switch base { - case 2: - // Add a leading 0b. - i-- - buf[i] = 'b' - i-- - buf[i] = '0' - case 8: - if buf[i] != '0' { - i-- - buf[i] = '0' - } - case 16: - // Add a leading 0x or 0X. - i-- - buf[i] = digits[16] - i-- - buf[i] = '0' - } - } - if verb == 'O' { - i-- - buf[i] = 'o' - i-- - buf[i] = '0' - } - - if negative { - i-- - buf[i] = '-' - } else if f.plus { - i-- - buf[i] = '+' - } else if f.space { - i-- - buf[i] = ' ' - } - - // Left padding with zeros has already been handled like precision earlier - // or the f.zero flag is ignored due to an explicitly set precision. - oldZero := f.zero - f.zero = false - f.pad(buf[i:]) - f.zero = oldZero -} - -// truncateString truncates the string s to the specified precision, if present. -func (f *fmt) truncateString(s string) string { - if f.precPresent { - n := f.prec - for i := range s { - n-- - if n < 0 { - return s[:i] - } - } - } - return s -} - -// truncate truncates the byte slice b as a string of the specified precision, if present. -func (f *fmt) truncate(b []byte) []byte { - if f.precPresent { - n := f.prec - for i := 0; i < len(b); { - n-- - if n < 0 { - return b[:i] - } - wid := 1 - if b[i] >= utf8.RuneSelf { - _, wid = utf8.DecodeRune(b[i:]) - } - i += wid - } - } - return b -} - -// fmtS formats a string. -func (f *fmt) fmtS(s string) { - s = f.truncateString(s) - f.padString(s) -} - -// fmtBs formats the byte slice b as if it was formatted as string with fmtS. -func (f *fmt) fmtBs(b []byte) { - b = f.truncate(b) - f.pad(b) -} - -// fmtSbx formats a string or byte slice as a hexadecimal encoding of its bytes. -func (f *fmt) fmtSbx(s string, b []byte, digits string) { - length := len(b) - if b == nil { - // No byte slice present. Assume string s should be encoded. - length = len(s) - } - // Set length to not process more bytes than the precision demands. - if f.precPresent && f.prec < length { - length = f.prec - } - // Compute width of the encoding taking into account the f.sharp and f.space flag. - width := 2 * length - if width > 0 { - if f.space { - // Each element encoded by two hexadecimals will get a leading 0x or 0X. - if f.sharp { - width *= 2 - } - // Elements will be separated by a space. - width += length - 1 - } else if f.sharp { - // Only a leading 0x or 0X will be added for the whole string. - width += 2 - } - } else { // The byte slice or string that should be encoded is empty. - if f.widPresent { - f.writePadding(f.wid) - } - return - } - // Handle padding to the left. - if f.widPresent && f.wid > width && !f.minus { - f.writePadding(f.wid - width) - } - // Write the encoding directly into the output buffer. - buf := *f.buf - if f.sharp { - // Add leading 0x or 0X. - buf = append(buf, '0', digits[16]) - } - var c byte - for i := 0; i < length; i++ { - if f.space && i > 0 { - // Separate elements with a space. - buf = append(buf, ' ') - if f.sharp { - // Add leading 0x or 0X for each element. - buf = append(buf, '0', digits[16]) - } - } - if b != nil { - c = b[i] // Take a byte from the input byte slice. - } else { - c = s[i] // Take a byte from the input string. - } - // Encode each byte as two hexadecimal digits. - buf = append(buf, digits[c>>4], digits[c&0xF]) - } - *f.buf = buf - // Handle padding to the right. - if f.widPresent && f.wid > width && f.minus { - f.writePadding(f.wid - width) - } -} - -// fmtSx formats a string as a hexadecimal encoding of its bytes. -func (f *fmt) fmtSx(s, digits string) { - f.fmtSbx(s, nil, digits) -} - -// fmtBx formats a byte slice as a hexadecimal encoding of its bytes. -func (f *fmt) fmtBx(b []byte, digits string) { - f.fmtSbx("", b, digits) -} - -// fmtQ formats a string as a double-quoted, escaped Go string constant. -// If f.sharp is set a raw (backquoted) string may be returned instead -// if the string does not contain any control characters other than tab. -func (f *fmt) fmtQ(s string) { - s = f.truncateString(s) - if f.sharp && strconv.CanBackquote(s) { - f.padString("`" + s + "`") - return - } - buf := f.intbuf[:0] - if f.plus { - f.pad(strconv.AppendQuoteToASCII(buf, s)) - } else { - f.pad(strconv.AppendQuote(buf, s)) - } -} - -// fmtC formats an integer as a Unicode character. -// If the character is not valid Unicode, it will print '\ufffd'. -func (f *fmt) fmtC(c uint64) { - // Explicitly check whether c exceeds utf8.MaxRune since the conversion - // of a uint64 to a rune may lose precision that indicates an overflow. - r := rune(c) - if c > utf8.MaxRune { - r = utf8.RuneError - } - buf := f.intbuf[:0] - f.pad(utf8.AppendRune(buf, r)) -} - -// fmtQc formats an integer as a single-quoted, escaped Go character constant. -// If the character is not valid Unicode, it will print '\ufffd'. -func (f *fmt) fmtQc(c uint64) { - r := rune(c) - if c > utf8.MaxRune { - r = utf8.RuneError - } - buf := f.intbuf[:0] - if f.plus { - f.pad(strconv.AppendQuoteRuneToASCII(buf, r)) - } else { - f.pad(strconv.AppendQuoteRune(buf, r)) - } -} - -// fmtFloat formats a float64. It assumes that verb is a valid format specifier -// for strconv.AppendFloat and therefore fits into a byte. -func (f *fmt) fmtFloat(v float64, size int, verb rune, prec int) { - // Explicit precision in format specifier overrules default precision. - if f.precPresent { - prec = f.prec - } - // Format number, reserving space for leading + sign if needed. - num := strconv.AppendFloat(f.intbuf[:1], v, byte(verb), prec, size) - if num[1] == '-' || num[1] == '+' { - num = num[1:] - } else { - num[0] = '+' - } - // f.space means to add a leading space instead of a "+" sign unless - // the sign is explicitly asked for by f.plus. - if f.space && num[0] == '+' && !f.plus { - num[0] = ' ' - } - // Special handling for infinities and NaN, - // which don't look like a number so shouldn't be padded with zeros. - if num[1] == 'I' || num[1] == 'N' { - oldZero := f.zero - f.zero = false - // Remove sign before NaN if not asked for. - if num[1] == 'N' && !f.space && !f.plus { - num = num[1:] - } - f.pad(num) - f.zero = oldZero - return - } - // The sharp flag forces printing a decimal point for non-binary formats - // and retains trailing zeros, which we may need to restore. - if f.sharp && verb != 'b' { - digits := 0 - switch verb { - case 'v', 'g', 'G', 'x': - digits = prec - // If no precision is set explicitly use a precision of 6. - if digits == -1 { - digits = 6 - } - } - - // Buffer pre-allocated with enough room for - // exponent notations of the form "e+123" or "p-1023". - var tailBuf [6]byte - tail := tailBuf[:0] - - hasDecimalPoint := false - sawNonzeroDigit := false - // Starting from i = 1 to skip sign at num[0]. - for i := 1; i < len(num); i++ { - switch num[i] { - case '.': - hasDecimalPoint = true - case 'p', 'P': - tail = append(tail, num[i:]...) - num = num[:i] - case 'e', 'E': - if verb != 'x' && verb != 'X' { - tail = append(tail, num[i:]...) - num = num[:i] - break - } - fallthrough - default: - if num[i] != '0' { - sawNonzeroDigit = true - } - // Count significant digits after the first non-zero digit. - if sawNonzeroDigit { - digits-- - } - } - } - if !hasDecimalPoint { - // Leading digit 0 should contribute once to digits. - if len(num) == 2 && num[1] == '0' { - digits-- - } - num = append(num, '.') - } - for digits > 0 { - num = append(num, '0') - digits-- - } - num = append(num, tail...) - } - // We want a sign if asked for and if the sign is not positive. - if f.plus || num[0] != '+' { - // If we're zero padding to the left we want the sign before the leading zeros. - // Achieve this by writing the sign out and then padding the unsigned number. - if f.zero && f.widPresent && f.wid > len(num) { - f.buf.writeByte(num[0]) - f.writePadding(f.wid - len(num)) - f.buf.write(num[1:]) - return - } - f.pad(num) - return - } - // No sign to show and the number is positive; just print the unsigned number. - f.pad(num[1:]) -} diff --git a/runtime/internal/lib/fmt/print.go b/runtime/internal/lib/fmt/print.go deleted file mode 100644 index 450d5a0d..00000000 --- a/runtime/internal/lib/fmt/print.go +++ /dev/null @@ -1,1243 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package fmt - -import ( - "io" - "os" - "reflect" - "unicode/utf8" -) - -// Strings for use with buffer.WriteString. -// This is less overhead than using buffer.Write with byte arrays. -const ( - commaSpaceString = ", " - nilAngleString = "" - nilParenString = "(nil)" - nilString = "nil" - mapString = "map[" - percentBangString = "%!" - missingString = "(MISSING)" - badIndexString = "(BADINDEX)" - panicString = "(PANIC=" - extraString = "%!(EXTRA " - badWidthString = "%!(BADWIDTH)" - badPrecString = "%!(BADPREC)" - noVerbString = "%!(NOVERB)" - invReflectString = "" -) - -// State represents the printer state passed to custom formatters. -// It provides access to the io.Writer interface plus information about -// the flags and options for the operand's format specifier. -type State interface { - // Write is the function to call to emit formatted output to be printed. - Write(b []byte) (n int, err error) - // Width returns the value of the width option and whether it has been set. - Width() (wid int, ok bool) - // Precision returns the value of the precision option and whether it has been set. - Precision() (prec int, ok bool) - - // Flag reports whether the flag c, a character, has been set. - Flag(c int) bool -} - -// Formatter is implemented by any value that has a Format method. -// The implementation controls how State and rune are interpreted, -// and may call Sprint() or Fprint(f) etc. to generate its output. -type Formatter interface { - Format(f State, verb rune) -} - -// Stringer is implemented by any value that has a String method, -// which defines the “native” format for that value. -// The String method is used to print values passed as an operand -// to any format that accepts a string or to an unformatted printer -// such as Print. -type Stringer interface { - String() string -} - -// GoStringer is implemented by any value that has a GoString method, -// which defines the Go syntax for that value. -// The GoString method is used to print values passed as an operand -// to a %#v format. -type GoStringer interface { - GoString() string -} - -/* TODO(xsw): -// FormatString returns a string representing the fully qualified formatting -// directive captured by the State, followed by the argument verb. (State does not -// itself contain the verb.) The result has a leading percent sign followed by any -// flags, the width, and the precision. Missing flags, width, and precision are -// omitted. This function allows a Formatter to reconstruct the original -// directive triggering the call to Format. -func FormatString(state State, verb rune) string { - var tmp [16]byte // Use a local buffer. - b := append(tmp[:0], '%') - for _, c := range " +-#0" { // All known flags - if state.Flag(int(c)) { // The argument is an int for historical reasons. - b = append(b, byte(c)) - } - } - if w, ok := state.Width(); ok { - b = strconv.AppendInt(b, int64(w), 10) - } - if p, ok := state.Precision(); ok { - b = append(b, '.') - b = strconv.AppendInt(b, int64(p), 10) - } - b = utf8.AppendRune(b, verb) - return string(b) -} -*/ - -// Use simple []byte instead of bytes.Buffer to avoid large dependency. -type buffer []byte - -func (b *buffer) write(p []byte) { - *b = append(*b, p...) -} - -func (b *buffer) writeString(s string) { - *b = append(*b, s...) -} - -func (b *buffer) writeByte(c byte) { - *b = append(*b, c) -} - -func (bp *buffer) writeRune(r rune) { - *bp = utf8.AppendRune(*bp, r) -} - -// pp is used to store a printer's state and is reused with sync.Pool to avoid allocations. -type pp struct { - buf buffer - - // arg holds the current item, as an interface{}. - arg any - - // value is used instead of arg for reflect values. - value reflect.Value - - // fmt is used to format basic items such as integers or strings. - fmt fmt - - // reordered records whether the format string used argument reordering. - reordered bool - // goodArgNum records whether the most recent reordering directive was valid. - goodArgNum bool - // panicking is set by catchPanic to avoid infinite panic, recover, panic, ... recursion. - panicking bool - // erroring is set when printing an error string to guard against calling handleMethods. - erroring bool - // wrapErrs is set when the format string may contain a %w verb. - wrapErrs bool - // wrappedErrs records the targets of the %w verb. - wrappedErrs []int -} - -/* TODO(xsw): -var ppFree = sync.Pool{ - New: func() any { return new(pp) }, -} -*/ - -// newPrinter allocates a new pp struct or grabs a cached one. -func newPrinter() *pp { - // TODO(xsw): - // p := ppFree.Get().(*pp) - p := &pp{} - p.panicking = false - p.erroring = false - p.wrapErrs = false - p.fmt.init(&p.buf) - return p -} - -// free saves used pp structs in ppFree; avoids an allocation per invocation. -func (p *pp) free() { - // Proper usage of a sync.Pool requires each entry to have approximately - // the same memory cost. To obtain this property when the stored type - // contains a variably-sized buffer, we add a hard limit on the maximum - // buffer to place back in the pool. If the buffer is larger than the - // limit, we drop the buffer and recycle just the printer. - // - // See https://golang.org/issue/23199. - if cap(p.buf) > 64*1024 { - p.buf = nil - } else { - p.buf = p.buf[:0] - } - if cap(p.wrappedErrs) > 8 { - p.wrappedErrs = nil - } - - p.arg = nil - // TODO(xsw): - // p.value = reflect.Value{} - p.wrappedErrs = p.wrappedErrs[:0] - // TODO(xsw): - // ppFree.Put(p) -} - -func (p *pp) Width() (wid int, ok bool) { return p.fmt.wid, p.fmt.widPresent } - -func (p *pp) Precision() (prec int, ok bool) { return p.fmt.prec, p.fmt.precPresent } - -func (p *pp) Flag(b int) bool { - switch b { - case '-': - return p.fmt.minus - case '+': - return p.fmt.plus || p.fmt.plusV - case '#': - return p.fmt.sharp || p.fmt.sharpV - case ' ': - return p.fmt.space - case '0': - return p.fmt.zero - } - return false -} - -// Implement Write so we can call Fprintf on a pp (through State), for -// recursive use in custom verbs. -func (p *pp) Write(b []byte) (ret int, err error) { - p.buf.write(b) - return len(b), nil -} - -// Implement WriteString so that we can call io.WriteString -// on a pp (through state), for efficiency. -func (p *pp) WriteString(s string) (ret int, err error) { - p.buf.writeString(s) - return len(s), nil -} - -// These routines end in 'f' and take a format string. - -// Fprintf formats according to a format specifier and writes to w. -// It returns the number of bytes written and any write error encountered. -func Fprintf(w io.Writer, format string, a ...any) (n int, err error) { - p := newPrinter() - p.doPrintf(format, a) - n, err = w.Write(p.buf) - p.free() - return -} - -// Printf formats according to a format specifier and writes to standard output. -// It returns the number of bytes written and any write error encountered. -func Printf(format string, a ...any) (n int, err error) { - return Fprintf(os.Stdout, format, a...) -} - -// Sprintf formats according to a format specifier and returns the resulting string. -func Sprintf(format string, a ...any) string { - p := newPrinter() - p.doPrintf(format, a) - s := string(p.buf) - p.free() - return s -} - -// Appendf formats according to a format specifier, appends the result to the byte -// slice, and returns the updated slice. -func Appendf(b []byte, format string, a ...any) []byte { - p := newPrinter() - p.doPrintf(format, a) - b = append(b, p.buf...) - p.free() - return b -} - -// These routines do not take a format string - -// Fprint formats using the default formats for its operands and writes to w. -// Spaces are added between operands when neither is a string. -// It returns the number of bytes written and any write error encountered. -func Fprint(w io.Writer, a ...any) (n int, err error) { - p := newPrinter() - p.doPrint(a) - n, err = w.Write(p.buf) - p.free() - return -} - -// Print formats using the default formats for its operands and writes to standard output. -// Spaces are added between operands when neither is a string. -// It returns the number of bytes written and any write error encountered. -func Print(a ...any) (n int, err error) { - return Fprint(os.Stdout, a...) -} - -// Sprint formats using the default formats for its operands and returns the resulting string. -// Spaces are added between operands when neither is a string. -func Sprint(a ...any) string { - p := newPrinter() - p.doPrint(a) - s := string(p.buf) - p.free() - return s -} - -// Append formats using the default formats for its operands, appends the result to -// the byte slice, and returns the updated slice. -func Append(b []byte, a ...any) []byte { - p := newPrinter() - p.doPrint(a) - b = append(b, p.buf...) - p.free() - return b -} - -// These routines end in 'ln', do not take a format string, -// always add spaces between operands, and add a newline -// after the last operand. - -// Fprintln formats using the default formats for its operands and writes to w. -// Spaces are always added between operands and a newline is appended. -// It returns the number of bytes written and any write error encountered. -func Fprintln(w io.Writer, a ...any) (n int, err error) { - p := newPrinter() - p.doPrintln(a) - n, err = w.Write(p.buf) - p.free() - return -} - -// Println formats using the default formats for its operands and writes to standard output. -// Spaces are always added between operands and a newline is appended. -// It returns the number of bytes written and any write error encountered. -func Println(a ...any) (n int, err error) { - return Fprintln(os.Stdout, a...) -} - -// Sprintln formats using the default formats for its operands and returns the resulting string. -// Spaces are always added between operands and a newline is appended. -func Sprintln(a ...any) string { - p := newPrinter() - p.doPrintln(a) - s := string(p.buf) - p.free() - return s -} - -// Appendln formats using the default formats for its operands, appends the result -// to the byte slice, and returns the updated slice. Spaces are always added -// between operands and a newline is appended. -func Appendln(b []byte, a ...any) []byte { - p := newPrinter() - p.doPrintln(a) - b = append(b, p.buf...) - p.free() - return b -} - -/* TODO(xsw): -// getField gets the i'th field of the struct value. -// If the field is itself is an interface, return a value for -// the thing inside the interface, not the interface itself. -func getField(v reflect.Value, i int) reflect.Value { - val := v.Field(i) - if val.Kind() == reflect.Interface && !val.IsNil() { - val = val.Elem() - } - return val -} -*/ - -// tooLarge reports whether the magnitude of the integer is -// too large to be used as a formatting width or precision. -func tooLarge(x int) bool { - const max int = 1e6 - return x > max || x < -max -} - -// parsenum converts ASCII to integer. num is 0 (and isnum is false) if no number present. -func parsenum(s string, start, end int) (num int, isnum bool, newi int) { - if start >= end { - return 0, false, end - } - for newi = start; newi < end && '0' <= s[newi] && s[newi] <= '9'; newi++ { - if tooLarge(num) { - return 0, false, end // Overflow; crazy long number most likely. - } - num = num*10 + int(s[newi]-'0') - isnum = true - } - return -} - -func (p *pp) unknownType(v reflect.Value) { - if !v.IsValid() { - p.buf.writeString(nilAngleString) - return - } - p.buf.writeByte('?') - p.buf.writeString(v.Type().String()) - p.buf.writeByte('?') -} - -func (p *pp) badVerb(verb rune) { - /* - p.erroring = true - p.buf.writeString(percentBangString) - p.buf.writeRune(verb) - p.buf.writeByte('(') - switch { - case p.arg != nil: - p.buf.writeString(reflect.TypeOf(p.arg).String()) - p.buf.writeByte('=') - p.printArg(p.arg, 'v') - case p.value.IsValid(): - p.buf.writeString(p.value.Type().String()) - p.buf.writeByte('=') - p.printValue(p.value, 'v', 0) - default: - p.buf.writeString(nilAngleString) - } - p.buf.writeByte(')') - p.erroring = false - */ - panic("todo: fmt.(*pp).badVerb") -} - -func (p *pp) fmtBool(v bool, verb rune) { - switch verb { - case 't', 'v': - p.fmt.fmtBoolean(v) - default: - p.badVerb(verb) - } -} - -// fmt0x64 formats a uint64 in hexadecimal and prefixes it with 0x or -// not, as requested, by temporarily setting the sharp flag. -func (p *pp) fmt0x64(v uint64, leading0x bool) { - sharp := p.fmt.sharp - p.fmt.sharp = leading0x - p.fmt.fmtInteger(v, 16, unsigned, 'v', ldigits) - p.fmt.sharp = sharp -} - -// fmtInteger formats a signed or unsigned integer. -func (p *pp) fmtInteger(v uint64, isSigned bool, verb rune) { - switch verb { - case 'v': - if p.fmt.sharpV && !isSigned { - p.fmt0x64(v, true) - } else { - p.fmt.fmtInteger(v, 10, isSigned, verb, ldigits) - } - case 'd': - p.fmt.fmtInteger(v, 10, isSigned, verb, ldigits) - case 'b': - p.fmt.fmtInteger(v, 2, isSigned, verb, ldigits) - case 'o', 'O': - p.fmt.fmtInteger(v, 8, isSigned, verb, ldigits) - case 'x': - p.fmt.fmtInteger(v, 16, isSigned, verb, ldigits) - case 'X': - p.fmt.fmtInteger(v, 16, isSigned, verb, udigits) - case 'c': - p.fmt.fmtC(v) - case 'q': - p.fmt.fmtQc(v) - case 'U': - p.fmt.fmtUnicode(v) - default: - p.badVerb(verb) - } -} - -// fmtFloat formats a float. The default precision for each verb -// is specified as last argument in the call to fmt_float. -func (p *pp) fmtFloat(v float64, size int, verb rune) { - switch verb { - case 'v': - p.fmt.fmtFloat(v, size, 'g', -1) - case 'b', 'g', 'G', 'x', 'X': - p.fmt.fmtFloat(v, size, verb, -1) - case 'f', 'e', 'E': - p.fmt.fmtFloat(v, size, verb, 6) - case 'F': - p.fmt.fmtFloat(v, size, 'f', 6) - default: - p.badVerb(verb) - } -} - -// fmtComplex formats a complex number v with -// r = real(v) and j = imag(v) as (r+ji) using -// fmtFloat for r and j formatting. -func (p *pp) fmtComplex(v complex128, size int, verb rune) { - // Make sure any unsupported verbs are found before the - // calls to fmtFloat to not generate an incorrect error string. - switch verb { - case 'v', 'b', 'g', 'G', 'x', 'X', 'f', 'F', 'e', 'E': - oldPlus := p.fmt.plus - p.buf.writeByte('(') - p.fmtFloat(real(v), size/2, verb) - // Imaginary part always has a sign. - p.fmt.plus = true - p.fmtFloat(imag(v), size/2, verb) - p.buf.writeString("i)") - p.fmt.plus = oldPlus - default: - p.badVerb(verb) - } -} - -func (p *pp) fmtString(v string, verb rune) { - switch verb { - case 'v': - if p.fmt.sharpV { - p.fmt.fmtQ(v) - } else { - p.fmt.fmtS(v) - } - case 's': - p.fmt.fmtS(v) - case 'x': - p.fmt.fmtSx(v, ldigits) - case 'X': - p.fmt.fmtSx(v, udigits) - case 'q': - p.fmt.fmtQ(v) - default: - p.badVerb(verb) - } -} - -func (p *pp) fmtBytes(v []byte, verb rune, typeString string) { - switch verb { - case 'v', 'd': - if p.fmt.sharpV { - p.buf.writeString(typeString) - if v == nil { - p.buf.writeString(nilParenString) - return - } - p.buf.writeByte('{') - for i, c := range v { - if i > 0 { - p.buf.writeString(commaSpaceString) - } - p.fmt0x64(uint64(c), true) - } - p.buf.writeByte('}') - } else { - p.buf.writeByte('[') - for i, c := range v { - if i > 0 { - p.buf.writeByte(' ') - } - p.fmt.fmtInteger(uint64(c), 10, unsigned, verb, ldigits) - } - p.buf.writeByte(']') - } - case 's': - p.fmt.fmtBs(v) - case 'x': - p.fmt.fmtBx(v, ldigits) - case 'X': - p.fmt.fmtBx(v, udigits) - case 'q': - p.fmt.fmtQ(string(v)) - default: - // p.printValue(reflect.ValueOf(v), verb, 0) - panic("todo: fmt.(*pp).fmtBytes") - } -} - -func (p *pp) fmtPointer(value reflect.Value, verb rune) { - var u uintptr - switch value.Kind() { - case reflect.Chan, reflect.Func, reflect.Map, reflect.Pointer, reflect.Slice, reflect.UnsafePointer: - u = value.Pointer() - default: - p.badVerb(verb) - return - } - - switch verb { - case 'v': - if p.fmt.sharpV { - p.buf.writeByte('(') - p.buf.writeString(value.Type().String()) - p.buf.writeString(")(") - if u == 0 { - p.buf.writeString(nilString) - } else { - p.fmt0x64(uint64(u), true) - } - p.buf.writeByte(')') - } else { - if u == 0 { - p.fmt.padString(nilAngleString) - } else { - p.fmt0x64(uint64(u), !p.fmt.sharp) - } - } - case 'p': - p.fmt0x64(uint64(u), !p.fmt.sharp) - case 'b', 'o', 'd', 'x', 'X': - p.fmtInteger(uint64(u), unsigned, verb) - default: - p.badVerb(verb) - } -} - -func (p *pp) catchPanic(arg any, verb rune, method string) { - if err := recover(); err != nil { - // If it's a nil pointer, just say "". The likeliest causes are a - // Stringer that fails to guard against nil or a nil pointer for a - // value receiver, and in either case, "" is a nice result. - if v := reflect.ValueOf(arg); v.Kind() == reflect.Pointer && v.IsNil() { - p.buf.writeString(nilAngleString) - return - } - // Otherwise print a concise panic message. Most of the time the panic - // value will print itself nicely. - if p.panicking { - // Nested panics; the recursion in printArg cannot succeed. - panic(err) - } - - oldFlags := p.fmt.fmtFlags - // For this output we want default behavior. - p.fmt.clearflags() - - p.buf.writeString(percentBangString) - p.buf.writeRune(verb) - p.buf.writeString(panicString) - p.buf.writeString(method) - p.buf.writeString(" method: ") - p.panicking = true - p.printArg(err, 'v') - p.panicking = false - p.buf.writeByte(')') - - p.fmt.fmtFlags = oldFlags - } -} - -func (p *pp) handleMethods(verb rune) (handled bool) { - if p.erroring { - return - } - if verb == 'w' { - // It is invalid to use %w other than with Errorf or with a non-error arg. - _, ok := p.arg.(error) - if !ok || !p.wrapErrs { - p.badVerb(verb) - return true - } - // If the arg is a Formatter, pass 'v' as the verb to it. - verb = 'v' - } - - // Is it a Formatter? - if formatter, ok := p.arg.(Formatter); ok { - handled = true - defer p.catchPanic(p.arg, verb, "Format") - formatter.Format(p, verb) - return - } - - // If we're doing Go syntax and the argument knows how to supply it, take care of it now. - if p.fmt.sharpV { - if stringer, ok := p.arg.(GoStringer); ok { - handled = true - defer p.catchPanic(p.arg, verb, "GoString") - // Print the result of GoString unadorned. - p.fmt.fmtS(stringer.GoString()) - return - } - } else { - // If a string is acceptable according to the format, see if - // the value satisfies one of the string-valued interfaces. - // Println etc. set verb to %v, which is "stringable". - switch verb { - case 'v', 's', 'x', 'X', 'q': - // Is it an error or Stringer? - // The duplication in the bodies is necessary: - // setting handled and deferring catchPanic - // must happen before calling the method. - switch v := p.arg.(type) { - case error: - handled = true - defer p.catchPanic(p.arg, verb, "Error") - p.fmtString(v.Error(), verb) - return - - case Stringer: - handled = true - defer p.catchPanic(p.arg, verb, "String") - p.fmtString(v.String(), verb) - return - } - } - } - return false -} - -func (p *pp) printArg(arg any, verb rune) { - p.arg = arg - p.value = reflect.Value{} - - if arg == nil { - switch verb { - case 'T', 'v': - p.fmt.padString(nilAngleString) - default: - p.badVerb(verb) - } - return - } - - // Special processing considerations. - // %T (the value's type) and %p (its address) are special; we always do them first. - switch verb { - case 'T': - p.fmt.fmtS(reflect.TypeOf(arg).String()) - return - case 'p': - p.fmtPointer(reflect.ValueOf(arg), 'p') - return - } - - // Some types can be done without reflection. - switch f := arg.(type) { - case bool: - p.fmtBool(f, verb) - case float32: - p.fmtFloat(float64(f), 32, verb) - case float64: - p.fmtFloat(f, 64, verb) - case complex64: - p.fmtComplex(complex128(f), 64, verb) - case complex128: - p.fmtComplex(f, 128, verb) - case int: - p.fmtInteger(uint64(f), signed, verb) - case int8: - p.fmtInteger(uint64(f), signed, verb) - case int16: - p.fmtInteger(uint64(f), signed, verb) - case int32: - p.fmtInteger(uint64(f), signed, verb) - case int64: - p.fmtInteger(uint64(f), signed, verb) - case uint: - p.fmtInteger(uint64(f), unsigned, verb) - case uint8: - p.fmtInteger(uint64(f), unsigned, verb) - case uint16: - p.fmtInteger(uint64(f), unsigned, verb) - case uint32: - p.fmtInteger(uint64(f), unsigned, verb) - case uint64: - p.fmtInteger(f, unsigned, verb) - case uintptr: - p.fmtInteger(uint64(f), unsigned, verb) - case string: - p.fmtString(f, verb) - case []byte: - p.fmtBytes(f, verb, "[]byte") - case reflect.Value: - // Handle extractable values with special methods - // since printValue does not handle them at depth 0. - if f.IsValid() && f.CanInterface() { - p.arg = f.Interface() - if p.handleMethods(verb) { - return - } - } - p.printValue(f, verb, 0) - default: - // If the type is not simple, it might have methods. - if !p.handleMethods(verb) { - // Need to use reflection, since the type had no - // interface methods that could be used for formatting. - p.printValue(reflect.ValueOf(f), verb, 0) - } - } -} - -// printValue is similar to printArg but starts with a reflect value, not an interface{} value. -// It does not handle 'p' and 'T' verbs because these should have been already handled by printArg. -func (p *pp) printValue(value reflect.Value, verb rune, depth int) { - // Handle values with special methods if not already handled by printArg (depth == 0). - if depth > 0 && value.IsValid() && value.CanInterface() { - p.arg = value.Interface() - if p.handleMethods(verb) { - return - } - } - p.arg = nil - p.value = value - - switch f := value; value.Kind() { - case reflect.Invalid: - if depth == 0 { - p.buf.writeString(invReflectString) - } else { - switch verb { - case 'v': - p.buf.writeString(nilAngleString) - default: - p.badVerb(verb) - } - } - case reflect.Bool: - p.fmtBool(f.Bool(), verb) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - p.fmtInteger(uint64(f.Int()), signed, verb) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - p.fmtInteger(f.Uint(), unsigned, verb) - case reflect.Float32: - p.fmtFloat(f.Float(), 32, verb) - case reflect.Float64: - p.fmtFloat(f.Float(), 64, verb) - case reflect.Complex64: - p.fmtComplex(f.Complex(), 64, verb) - case reflect.Complex128: - p.fmtComplex(f.Complex(), 128, verb) - case reflect.String: - p.fmtString(f.String(), verb) - case reflect.Map: - /* TODO(xsw): - if p.fmt.sharpV { - p.buf.writeString(f.Type().String()) - if f.IsNil() { - p.buf.writeString(nilParenString) - return - } - p.buf.writeByte('{') - } else { - p.buf.writeString(mapString) - } - sorted := fmtsort.Sort(f) - for i, key := range sorted.Key { - if i > 0 { - if p.fmt.sharpV { - p.buf.writeString(commaSpaceString) - } else { - p.buf.writeByte(' ') - } - } - p.printValue(key, verb, depth+1) - p.buf.writeByte(':') - p.printValue(sorted.Value[i], verb, depth+1) - } - if p.fmt.sharpV { - p.buf.writeByte('}') - } else { - p.buf.writeByte(']') - } - */ - panic("todo: fmt.(*pp).printValue - reflect.Map") - case reflect.Struct: - /* TODO(xsw): - if p.fmt.sharpV { - p.buf.writeString(f.Type().String()) - } - p.buf.writeByte('{') - for i := 0; i < f.NumField(); i++ { - if i > 0 { - if p.fmt.sharpV { - p.buf.writeString(commaSpaceString) - } else { - p.buf.writeByte(' ') - } - } - if p.fmt.plusV || p.fmt.sharpV { - if name := f.Type().Field(i).Name; name != "" { - p.buf.writeString(name) - p.buf.writeByte(':') - } - } - p.printValue(getField(f, i), verb, depth+1) - } - p.buf.writeByte('}') - */ - panic("todo: fmt.(*pp).printValue - reflect.Struct") - case reflect.Interface: - value := f.Elem() - if !value.IsValid() { - if p.fmt.sharpV { - p.buf.writeString(f.Type().String()) - p.buf.writeString(nilParenString) - } else { - p.buf.writeString(nilAngleString) - } - } else { - p.printValue(value, verb, depth+1) - } - case reflect.Array, reflect.Slice: - switch verb { - case 's', 'q', 'x', 'X': - // Handle byte and uint8 slices and arrays special for the above verbs. - t := f.Type() - if t.Elem().Kind() == reflect.Uint8 { - var bytes []byte - if f.Kind() == reflect.Slice { - bytes = f.Bytes() - } else if f.CanAddr() { - bytes = f.Slice(0, f.Len()).Bytes() - } else { - // We have an array, but we cannot Slice() a non-addressable array, - // so we build a slice by hand. This is a rare case but it would be nice - // if reflection could help a little more. - bytes = make([]byte, f.Len()) - for i := range bytes { - bytes[i] = byte(f.Index(i).Uint()) - } - } - p.fmtBytes(bytes, verb, t.String()) - return - } - } - if p.fmt.sharpV { - p.buf.writeString(f.Type().String()) - if f.Kind() == reflect.Slice && f.IsNil() { - p.buf.writeString(nilParenString) - return - } - p.buf.writeByte('{') - for i := 0; i < f.Len(); i++ { - if i > 0 { - p.buf.writeString(commaSpaceString) - } - p.printValue(f.Index(i), verb, depth+1) - } - p.buf.writeByte('}') - } else { - p.buf.writeByte('[') - for i := 0; i < f.Len(); i++ { - if i > 0 { - p.buf.writeByte(' ') - } - p.printValue(f.Index(i), verb, depth+1) - } - p.buf.writeByte(']') - } - case reflect.Pointer: - // pointer to array or slice or struct? ok at top level - // but not embedded (avoid loops) - if depth == 0 && f.Pointer() != 0 { - switch a := f.Elem(); a.Kind() { - case reflect.Array, reflect.Slice, reflect.Struct, reflect.Map: - p.buf.writeByte('&') - p.printValue(a, verb, depth+1) - return - } - } - fallthrough - case reflect.Chan, reflect.Func, reflect.UnsafePointer: - p.fmtPointer(f, verb) - default: - p.unknownType(f) - } -} - -// intFromArg gets the argNumth element of a. On return, isInt reports whether the argument has integer type. -func intFromArg(a []any, argNum int) (num int, isInt bool, newArgNum int) { - newArgNum = argNum - if argNum < len(a) { - num, isInt = a[argNum].(int) // Almost always OK. - if !isInt { - // Work harder. - switch v := reflect.ValueOf(a[argNum]); v.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - n := v.Int() - if int64(int(n)) == n { - num = int(n) - isInt = true - } - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - n := v.Uint() - if int64(n) >= 0 && uint64(int(n)) == n { - num = int(n) - isInt = true - } - default: - // Already 0, false. - } - } - newArgNum = argNum + 1 - if tooLarge(num) { - num = 0 - isInt = false - } - } - return -} - -// parseArgNumber returns the value of the bracketed number, minus 1 -// (explicit argument numbers are one-indexed but we want zero-indexed). -// The opening bracket is known to be present at format[0]. -// The returned values are the index, the number of bytes to consume -// up to the closing paren, if present, and whether the number parsed -// ok. The bytes to consume will be 1 if no closing paren is present. -func parseArgNumber(format string) (index int, wid int, ok bool) { - // There must be at least 3 bytes: [n]. - if len(format) < 3 { - return 0, 1, false - } - - // Find closing bracket. - for i := 1; i < len(format); i++ { - if format[i] == ']' { - width, ok, newi := parsenum(format, 1, i) - if !ok || newi != i { - return 0, i + 1, false - } - return width - 1, i + 1, true // arg numbers are one-indexed and skip paren. - } - } - return 0, 1, false -} - -// argNumber returns the next argument to evaluate, which is either the value of the passed-in -// argNum or the value of the bracketed integer that begins format[i:]. It also returns -// the new value of i, that is, the index of the next byte of the format to process. -func (p *pp) argNumber(argNum int, format string, i int, numArgs int) (newArgNum, newi int, found bool) { - if len(format) <= i || format[i] != '[' { - return argNum, i, false - } - p.reordered = true - index, wid, ok := parseArgNumber(format[i:]) - if ok && 0 <= index && index < numArgs { - return index, i + wid, true - } - p.goodArgNum = false - return argNum, i + wid, ok -} - -func (p *pp) badArgNum(verb rune) { - p.buf.writeString(percentBangString) - p.buf.writeRune(verb) - p.buf.writeString(badIndexString) -} - -func (p *pp) missingArg(verb rune) { - p.buf.writeString(percentBangString) - p.buf.writeRune(verb) - p.buf.writeString(missingString) -} - -func (p *pp) doPrintf(format string, a []any) { - end := len(format) - argNum := 0 // we process one argument per non-trivial format - afterIndex := false // previous item in format was an index like [3]. - p.reordered = false -formatLoop: - for i := 0; i < end; { - p.goodArgNum = true - lasti := i - for i < end && format[i] != '%' { - i++ - } - if i > lasti { - p.buf.writeString(format[lasti:i]) - } - if i >= end { - // done processing format string - break - } - - // Process one verb - i++ - - // Do we have flags? - p.fmt.clearflags() - simpleFormat: - for ; i < end; i++ { - c := format[i] - switch c { - case '#': - p.fmt.sharp = true - case '0': - p.fmt.zero = !p.fmt.minus // Only allow zero padding to the left. - case '+': - p.fmt.plus = true - case '-': - p.fmt.minus = true - p.fmt.zero = false // Do not pad with zeros to the right. - case ' ': - p.fmt.space = true - default: - // Fast path for common case of ascii lower case simple verbs - // without precision or width or argument indices. - if 'a' <= c && c <= 'z' && argNum < len(a) { - switch c { - case 'w': - p.wrappedErrs = append(p.wrappedErrs, argNum) - fallthrough - case 'v': - // Go syntax - p.fmt.sharpV = p.fmt.sharp - p.fmt.sharp = false - // Struct-field syntax - p.fmt.plusV = p.fmt.plus - p.fmt.plus = false - } - p.printArg(a[argNum], rune(c)) - argNum++ - i++ - continue formatLoop - } - // Format is more complex than simple flags and a verb or is malformed. - break simpleFormat - } - } - - // Do we have an explicit argument index? - argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a)) - - // Do we have width? - if i < end && format[i] == '*' { - i++ - p.fmt.wid, p.fmt.widPresent, argNum = intFromArg(a, argNum) - - if !p.fmt.widPresent { - p.buf.writeString(badWidthString) - } - - // We have a negative width, so take its value and ensure - // that the minus flag is set - if p.fmt.wid < 0 { - p.fmt.wid = -p.fmt.wid - p.fmt.minus = true - p.fmt.zero = false // Do not pad with zeros to the right. - } - afterIndex = false - } else { - p.fmt.wid, p.fmt.widPresent, i = parsenum(format, i, end) - if afterIndex && p.fmt.widPresent { // "%[3]2d" - p.goodArgNum = false - } - } - - // Do we have precision? - if i+1 < end && format[i] == '.' { - i++ - if afterIndex { // "%[3].2d" - p.goodArgNum = false - } - argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a)) - if i < end && format[i] == '*' { - i++ - p.fmt.prec, p.fmt.precPresent, argNum = intFromArg(a, argNum) - // Negative precision arguments don't make sense - if p.fmt.prec < 0 { - p.fmt.prec = 0 - p.fmt.precPresent = false - } - if !p.fmt.precPresent { - p.buf.writeString(badPrecString) - } - afterIndex = false - } else { - p.fmt.prec, p.fmt.precPresent, i = parsenum(format, i, end) - if !p.fmt.precPresent { - p.fmt.prec = 0 - p.fmt.precPresent = true - } - } - } - - if !afterIndex { - argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a)) - } - - if i >= end { - p.buf.writeString(noVerbString) - break - } - - verb, size := rune(format[i]), 1 - if verb >= utf8.RuneSelf { - verb, size = utf8.DecodeRuneInString(format[i:]) - } - i += size - - switch { - case verb == '%': // Percent does not absorb operands and ignores f.wid and f.prec. - p.buf.writeByte('%') - case !p.goodArgNum: - p.badArgNum(verb) - case argNum >= len(a): // No argument left over to print for the current verb. - p.missingArg(verb) - case verb == 'w': - p.wrappedErrs = append(p.wrappedErrs, argNum) - fallthrough - case verb == 'v': - // Go syntax - p.fmt.sharpV = p.fmt.sharp - p.fmt.sharp = false - // Struct-field syntax - p.fmt.plusV = p.fmt.plus - p.fmt.plus = false - fallthrough - default: - p.printArg(a[argNum], verb) - argNum++ - } - } - - // Check for extra arguments unless the call accessed the arguments - // out of order, in which case it's too expensive to detect if they've all - // been used and arguably OK if they're not. - if !p.reordered && argNum < len(a) { - p.fmt.clearflags() - p.buf.writeString(extraString) - for i, arg := range a[argNum:] { - if i > 0 { - p.buf.writeString(commaSpaceString) - } - if arg == nil { - p.buf.writeString(nilAngleString) - } else { - p.buf.writeString(reflect.TypeOf(arg).String()) - p.buf.writeByte('=') - p.printArg(arg, 'v') - } - } - p.buf.writeByte(')') - } -} - -func (p *pp) doPrint(a []any) { - prevString := false - for argNum, arg := range a { - isString := arg != nil && reflect.TypeOf(arg).Kind() == reflect.String - // Add a space between two non-string arguments. - if argNum > 0 && !isString && !prevString { - p.buf.writeByte(' ') - } - p.printArg(arg, 'v') - prevString = isString - } -} - -// doPrintln is like doPrint but always adds a space between arguments -// and a newline after the last argument. -func (p *pp) doPrintln(a []any) { - for argNum, arg := range a { - if argNum > 0 { - p.buf.writeByte(' ') - } - p.printArg(arg, 'v') - } - p.buf.writeByte('\n') -} diff --git a/runtime/internal/lib/hash/crc32/crc32.go b/runtime/internal/lib/hash/crc32/crc32.go index a26ad5e7..614b69e5 100644 --- a/runtime/internal/lib/hash/crc32/crc32.go +++ b/runtime/internal/lib/hash/crc32/crc32.go @@ -16,7 +16,6 @@ package crc32 -// llgo:skipall import ( "hash" @@ -24,6 +23,9 @@ import ( "github.com/goplus/llgo/runtime/internal/clite/zlib" ) +// llgo:skipall +type _crc32 struct{} + // The size of a CRC-32 checksum in bytes. const Size = 4 diff --git a/runtime/internal/lib/internal/abi/abi.go b/runtime/internal/lib/internal/abi/abi.go index e2bfb3b9..e6f333f5 100644 --- a/runtime/internal/lib/internal/abi/abi.go +++ b/runtime/internal/lib/internal/abi/abi.go @@ -16,16 +16,28 @@ package abi -// llgo:skipall import ( "unsafe" "github.com/goplus/llgo/runtime/abi" ) +// llgo:skipall +type _abi struct{} + type InterfaceType = abi.InterfaceType func NoEscape(p unsafe.Pointer) unsafe.Pointer { x := uintptr(p) return unsafe.Pointer(x ^ 0) } + +func FuncPCABI0(f interface{}) uintptr { + words := (*[2]unsafe.Pointer)(unsafe.Pointer(&f)) + return *(*uintptr)(unsafe.Pointer(words[1])) +} + +func FuncPCABIInternal(f interface{}) uintptr { + words := (*[2]unsafe.Pointer)(unsafe.Pointer(&f)) + return *(*uintptr)(unsafe.Pointer(words[1])) +} diff --git a/runtime/internal/lib/internal/bytealg/bytealg.go b/runtime/internal/lib/internal/bytealg/bytealg.go index a61da7a3..8434073c 100644 --- a/runtime/internal/lib/internal/bytealg/bytealg.go +++ b/runtime/internal/lib/internal/bytealg/bytealg.go @@ -16,7 +16,6 @@ package bytealg -// llgo:skip init CompareString import ( "unsafe" @@ -24,6 +23,9 @@ import ( "github.com/goplus/llgo/runtime/internal/runtime" ) +// llgo:skip init CompareString +type _bytealg struct{} + func IndexByte(b []byte, ch byte) int { ptr := unsafe.Pointer(unsafe.SliceData(b)) ret := c.Memchr(ptr, c.Int(ch), uintptr(len(b))) diff --git a/runtime/internal/lib/internal/cpu/cpu.go b/runtime/internal/lib/internal/cpu/cpu.go new file mode 100644 index 00000000..51dd3315 --- /dev/null +++ b/runtime/internal/lib/internal/cpu/cpu.go @@ -0,0 +1 @@ +package cpu diff --git a/runtime/internal/lib/internal/cpu/cpu_x86.go b/runtime/internal/lib/internal/cpu/cpu_x86.go new file mode 100644 index 00000000..129ff880 --- /dev/null +++ b/runtime/internal/lib/internal/cpu/cpu_x86.go @@ -0,0 +1,40 @@ +//go:build 386 || amd64 + +package cpu + +/* +#if defined(__GNUC__) || defined(__clang__) + static void getcpuid(unsigned int eax, unsigned int ecx, + unsigned int *a, unsigned int *b, + unsigned int *c, unsigned int *d) { + #if defined(__i386__) || defined(__x86_64__) + __asm__ __volatile__( + "pushq %%rbp\n\t" + "movq %%rsp, %%rbp\n\t" + "andq $-16, %%rsp\n\t" // 16-byte align stack + "cpuid\n\t" + "movq %%rbp, %%rsp\n\t" + "popq %%rbp\n\t" + : "=a"(*a), "=b"(*b), "=c"(*c), "=d"(*d) + : "a"(eax), "c"(ecx) + : "memory" + ); + #endif + } +#else + #error This code requires GCC or Clang +#endif +*/ +import "C" + +func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) { + C.getcpuid( + C.uint(eaxArg), + C.uint(ecxArg), + (*C.uint)(&eax), + (*C.uint)(&ebx), + (*C.uint)(&ecx), + (*C.uint)(&edx), + ) + return +} diff --git a/runtime/internal/lib/internal/fmtsort/sort.go b/runtime/internal/lib/internal/fmtsort/sort.go index 13456a45..802f724d 100644 --- a/runtime/internal/lib/internal/fmtsort/sort.go +++ b/runtime/internal/lib/internal/fmtsort/sort.go @@ -8,12 +8,14 @@ // that are valid map keys. package fmtsort -// llgo:skipall import ( "reflect" "sort" ) +// llgo:skipall +type _fmtsort struct{} + // Note: Throughout this package we avoid calling reflect.Value.Interface as // it is not always legal to do so and it's easier to avoid the issue than to face it. diff --git a/runtime/internal/lib/internal/godebug/godebug.go b/runtime/internal/lib/internal/godebug/godebug.go new file mode 100644 index 00000000..83311f86 --- /dev/null +++ b/runtime/internal/lib/internal/godebug/godebug.go @@ -0,0 +1,13 @@ +package godebug + +func setUpdate(update func(string, string)) { + println("todo: godebug.setUpdate") +} + +func registerMetric(name string, read func() uint64) { + println("todo: godebug.registerMetric") +} + +func setNewIncNonDefault(newIncNonDefault func(string) func()) { + println("todo: godebug.setNewIncNonDefault") +} diff --git a/runtime/internal/lib/internal/itoa/itoa.go b/runtime/internal/lib/internal/itoa/itoa.go index 32b15359..7eac6487 100644 --- a/runtime/internal/lib/internal/itoa/itoa.go +++ b/runtime/internal/lib/internal/itoa/itoa.go @@ -4,9 +4,11 @@ // Simple conversions to avoid depending on strconv. -// llgo:skipall package itoa +// llgo:skipall +type _itoa struct{} + // Itoa converts val to a decimal string. func Itoa(val int) string { if val < 0 { diff --git a/runtime/internal/lib/internal/oserror/errors.go b/runtime/internal/lib/internal/oserror/errors.go index b9124224..6e46ff9c 100644 --- a/runtime/internal/lib/internal/oserror/errors.go +++ b/runtime/internal/lib/internal/oserror/errors.go @@ -7,9 +7,11 @@ // These types are defined here to permit the syscall package to reference them. package oserror -// llgo:skipall import "errors" +// llgo:skipall +type _oserror struct{} + var ( ErrInvalid = errors.New("invalid argument") ErrPermission = errors.New("permission denied") diff --git a/runtime/internal/lib/internal/poll/poll.go b/runtime/internal/lib/internal/poll/poll.go new file mode 100644 index 00000000..666c4331 --- /dev/null +++ b/runtime/internal/lib/internal/poll/poll.go @@ -0,0 +1,45 @@ +package poll + +func runtime_Semacquire(sema *uint32) { + panic("todo: poll.runtime_Semacquire") +} + +func runtime_Semrelease(sema *uint32) { + panic("todo: poll.runtime_Semrelease") +} + +func runtime_pollServerInit() { + panic("todo: poll.runtime_pollServerInit") +} + +func runtime_pollOpen(fd uintptr) (uintptr, int) { + panic("todo: poll.runtime_pollOpen") +} + +func runtime_pollClose(ctx uintptr) { + panic("todo: poll.runtime_pollClose") +} + +func runtime_pollWait(ctx uintptr, mode int) int { + panic("todo: poll.runtime_pollWait") +} + +func runtime_pollWaitCanceled(ctx uintptr, mode int) { + panic("todo: poll.runtime_pollWaitCanceled") +} + +func runtime_pollReset(ctx uintptr, mode int) int { + panic("todo: poll.runtime_pollReset") +} + +func runtime_pollSetDeadline(ctx uintptr, d int64, mode int) { + panic("todo: poll.runtime_pollSetDeadline") +} + +func runtime_pollUnblock(ctx uintptr) { + panic("todo: poll.runtime_pollUnblock") +} + +func runtime_isPollServerDescriptor(fd uintptr) bool { + panic("todo: poll.runtime_isPollServerDescriptor") +} diff --git a/runtime/internal/lib/internal/syscall/execenv/execenv_default.go b/runtime/internal/lib/internal/syscall/execenv/execenv_default.go index 2a12ef55..25d0dddc 100644 --- a/runtime/internal/lib/internal/syscall/execenv/execenv_default.go +++ b/runtime/internal/lib/internal/syscall/execenv/execenv_default.go @@ -6,9 +6,11 @@ package execenv -// llgo:skipall import "syscall" +// llgo:skipall +type _execenv struct{} + // Default will return the default environment // variables based on the process attributes // provided. diff --git a/runtime/internal/lib/internal/syscall/execenv/execenv_windows.go b/runtime/internal/lib/internal/syscall/execenv/execenv_windows.go index 9cdfeece..c5ec1804 100644 --- a/runtime/internal/lib/internal/syscall/execenv/execenv_windows.go +++ b/runtime/internal/lib/internal/syscall/execenv/execenv_windows.go @@ -6,13 +6,15 @@ package execenv -// llgo:skipall import ( "internal/syscall/windows" "syscall" "unsafe" ) +// llgo:skipall +type _execenv struct{} + // Default will return the default environment // variables based on the process attributes // provided. diff --git a/runtime/internal/lib/internal/syscall/unix/_unix/fcntl_unix.c b/runtime/internal/lib/internal/syscall/unix/_unix/fcntl_unix.c new file mode 100644 index 00000000..fa1ad6f9 --- /dev/null +++ b/runtime/internal/lib/internal/syscall/unix/_unix/fcntl_unix.c @@ -0,0 +1,33 @@ +#include +#include +#include +#include + +struct fcntl_ret +{ + int32_t r1; // Return value + int32_t err; // Error code +}; + +// llgo_fcntl implements the fcntl system call wrapper for Go +// fd: file descriptor +// cmd: fcntl command +// arg: command argument +struct fcntl_ret llgo_fcntl2(int32_t fd, int32_t cmd, int32_t arg) +{ + struct fcntl_ret ret = {0}; + int result = fcntl(fd, cmd, arg); + + if (result == -1) + { + ret.err = errno; + ret.r1 = -1; + } + else + { + ret.err = 0; + ret.r1 = result; + } + + return ret; +} \ No newline at end of file diff --git a/runtime/internal/lib/internal/syscall/unix/nonblocking_js.go b/runtime/internal/lib/internal/syscall/unix/at_sysnum_darwin.go similarity index 53% rename from runtime/internal/lib/internal/syscall/unix/nonblocking_js.go rename to runtime/internal/lib/internal/syscall/unix/at_sysnum_darwin.go index cfe78c58..77b0af80 100644 --- a/runtime/internal/lib/internal/syscall/unix/nonblocking_js.go +++ b/runtime/internal/lib/internal/syscall/unix/at_sysnum_darwin.go @@ -2,14 +2,13 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build js && wasm - package unix -func IsNonblock(fd int) (nonblocking bool, err error) { - return false, nil -} +const ( + AT_EACCESS = 0x10 + AT_FDCWD = -0x2 + AT_REMOVEDIR = 0x80 + AT_SYMLINK_NOFOLLOW = 0x0020 -func HasNonblockFlag(flag int) bool { - return false -} + UTIME_OMIT = -0x2 +) diff --git a/runtime/internal/lib/internal/syscall/unix/at_sysnum_linux.go b/runtime/internal/lib/internal/syscall/unix/at_sysnum_linux.go new file mode 100644 index 00000000..7c3b15c3 --- /dev/null +++ b/runtime/internal/lib/internal/syscall/unix/at_sysnum_linux.go @@ -0,0 +1,19 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package unix + +import "syscall" + +const unlinkatTrap uintptr = syscall.SYS_UNLINKAT +const openatTrap uintptr = syscall.SYS_OPENAT + +const ( + AT_EACCESS = 0x200 + AT_FDCWD = -0x64 + AT_REMOVEDIR = 0x200 + AT_SYMLINK_NOFOLLOW = 0x100 + + UTIME_OMIT = 0x3ffffffe +) diff --git a/runtime/internal/lib/internal/syscall/unix/constants.go b/runtime/internal/lib/internal/syscall/unix/constants.go new file mode 100644 index 00000000..28092c2d --- /dev/null +++ b/runtime/internal/lib/internal/syscall/unix/constants.go @@ -0,0 +1,18 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build unix + +package unix + +const ( + R_OK = 0x4 + W_OK = 0x2 + X_OK = 0x1 + + // NoFollowErrno is the error returned from open/openat called with + // O_NOFOLLOW flag, when the trailing component (basename) of the path + // is a symbolic link. + NoFollowErrno = noFollowErrno +) diff --git a/runtime/internal/lib/internal/syscall/unix/fcntl_unix.go b/runtime/internal/lib/internal/syscall/unix/fcntl_unix.go new file mode 100644 index 00000000..96518556 --- /dev/null +++ b/runtime/internal/lib/internal/syscall/unix/fcntl_unix.go @@ -0,0 +1,23 @@ +package unix + +import ( + "syscall" + _ "unsafe" +) + +// llgo:skip fcntl +const ( + LLGoPackage = "link" + LLGoFiles = "_unix/fcntl_unix.c" +) + +//go:linkname fcntl C.llgo_fcntl2 +func fcntl(fd int32, cmd int32, arg int32) (int32, int32) + +func Fcntl(fd int, cmd int, arg int) (int, error) { + val, errno := fcntl(int32(fd), int32(cmd), int32(arg)) + if val == -1 { + return int(val), syscall.Errno(errno) + } + return int(val), nil +} diff --git a/runtime/internal/lib/internal/syscall/unix/nofollow_bsd.go b/runtime/internal/lib/internal/syscall/unix/nofollow_bsd.go new file mode 100644 index 00000000..32c4de11 --- /dev/null +++ b/runtime/internal/lib/internal/syscall/unix/nofollow_bsd.go @@ -0,0 +1,14 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build dragonfly || freebsd + +package unix + +import "syscall" + +// References: +// - https://man.freebsd.org/cgi/man.cgi?open(2) +// - https://man.dragonflybsd.org/?command=open§ion=2 +const noFollowErrno = syscall.EMLINK diff --git a/runtime/internal/lib/internal/syscall/unix/nofollow_netbsd.go b/runtime/internal/lib/internal/syscall/unix/nofollow_netbsd.go new file mode 100644 index 00000000..3ae91e79 --- /dev/null +++ b/runtime/internal/lib/internal/syscall/unix/nofollow_netbsd.go @@ -0,0 +1,10 @@ +// Copyright 2024 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 unix + +import "syscall" + +// Reference: https://man.netbsd.org/open.2 +const noFollowErrno = syscall.EFTYPE diff --git a/runtime/internal/lib/internal/syscall/unix/nofollow_posix.go b/runtime/internal/lib/internal/syscall/unix/nofollow_posix.go new file mode 100644 index 00000000..de2ea14f --- /dev/null +++ b/runtime/internal/lib/internal/syscall/unix/nofollow_posix.go @@ -0,0 +1,22 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build unix && !dragonfly && !freebsd && !netbsd + +package unix + +import "syscall" + +// POSIX.1-2008 says it's ELOOP. Most platforms follow: +// +// - aix: O_NOFOLLOW not documented (https://www.ibm.com/docs/ssw_aix_73/o_bostechref/open.html), assuming ELOOP +// - android: see linux +// - darwin: https://github.com/apple/darwin-xnu/blob/main/bsd/man/man2/open.2 +// - hurd: who knows if it works at all (https://www.gnu.org/software/hurd/open_issues/open_symlink.html) +// - illumos: https://illumos.org/man/2/open +// - ios: see darwin +// - linux: https://man7.org/linux/man-pages/man2/openat.2.html +// - openbsd: https://man.openbsd.org/open.2 +// - solaris: https://docs.oracle.com/cd/E23824_01/html/821-1463/open-2.html +const noFollowErrno = syscall.ELOOP diff --git a/runtime/internal/lib/internal/syscall/unix/nonblocking_unix.go b/runtime/internal/lib/internal/syscall/unix/nonblocking_unix.go deleted file mode 100644 index a7014ccd..00000000 --- a/runtime/internal/lib/internal/syscall/unix/nonblocking_unix.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build unix - -package unix - -import "github.com/goplus/llgo/runtime/internal/clite/syscall" - -/* TODO(xsw): -func IsNonblock(fd int) (nonblocking bool, err error) { - flag, e1 := Fcntl(fd, syscall.F_GETFL, 0) - if e1 != nil { - return false, e1 - } - return flag&syscall.O_NONBLOCK != 0, nil -} -*/ - -func HasNonblockFlag(flag int) bool { - return flag&syscall.O_NONBLOCK != 0 -} diff --git a/runtime/internal/lib/internal/syscall/unix/nonblocking_wasip1.go b/runtime/internal/lib/internal/syscall/unix/nonblocking_wasip1.go deleted file mode 100644 index 38accf58..00000000 --- a/runtime/internal/lib/internal/syscall/unix/nonblocking_wasip1.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2023 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build wasip1 - -package unix - -import "github.com/goplus/llgo/runtime/internal/clite/syscall" - -/* TODO(xsw): -import ( - "syscall" - _ "unsafe" // for go:linkname -) - -func IsNonblock(fd int) (nonblocking bool, err error) { - flags, e1 := fd_fdstat_get_flags(fd) - if e1 != nil { - return false, e1 - } - return flags&syscall.FDFLAG_NONBLOCK != 0, nil -} -*/ - -func HasNonblockFlag(flag int) bool { - return flag&syscall.FDFLAG_NONBLOCK != 0 -} - -/* TODO(xsw): -// This helper is implemented in the syscall package. It means we don't have -// to redefine the fd_fdstat_get host import or the fdstat struct it -// populates. -// -//-go:linkname fd_fdstat_get_flags syscall.fd_fdstat_get_flags -func fd_fdstat_get_flags(fd int) (uint32, error) -*/ diff --git a/runtime/internal/lib/internal/syscall/unix/unix.go b/runtime/internal/lib/internal/syscall/unix/unix.go index 1ee23aeb..ca29490a 100644 --- a/runtime/internal/lib/internal/syscall/unix/unix.go +++ b/runtime/internal/lib/internal/syscall/unix/unix.go @@ -17,8 +17,10 @@ package unix import ( + "syscall" _ "unsafe" ) -// llgo:skipall -type _unix struct{} +func HasNonblockFlag(flag int) bool { + return flag&syscall.O_NONBLOCK != 0 +} diff --git a/runtime/internal/lib/math/big/int.go b/runtime/internal/lib/math/big/int.go index e36a434f..46ade0dd 100644 --- a/runtime/internal/lib/math/big/int.go +++ b/runtime/internal/lib/math/big/int.go @@ -16,7 +16,6 @@ package big -// llgo:skipall import ( "math/rand" @@ -24,6 +23,9 @@ import ( "github.com/goplus/llgo/runtime/internal/clite/openssl" ) +// llgo:skipall +type _big struct{} + // A Word represents a single digit of a multi-precision unsigned integer. type Word openssl.BN_ULONG diff --git a/runtime/internal/lib/math/math.go b/runtime/internal/lib/math/math.go index e53e88c7..80ed223b 100644 --- a/runtime/internal/lib/math/math.go +++ b/runtime/internal/lib/math/math.go @@ -16,13 +16,13 @@ package math -// llgo:skip sin cos import ( _ "unsafe" c "github.com/goplus/llgo/runtime/internal/clite" ) +// llgo:skip sin cos const ( LLGoPackage = true ) diff --git a/runtime/internal/lib/math/rand/rand.go b/runtime/internal/lib/math/rand/rand.go index 3cba1a59..761e4abc 100644 --- a/runtime/internal/lib/math/rand/rand.go +++ b/runtime/internal/lib/math/rand/rand.go @@ -17,7 +17,6 @@ // crypto/rand package. package rand -// llgo:skipall import ( "sync" "sync/atomic" @@ -28,6 +27,9 @@ import ( "github.com/goplus/llgo/runtime/internal/clite/time" ) +// llgo:skipall +type _rand struct{} + // A Source represents a source of uniformly-distributed // pseudo-random int64 values in the range [0, 1<<63). // diff --git a/runtime/internal/lib/os/dir.go b/runtime/internal/lib/os/dir.go new file mode 100644 index 00000000..7dba50ae --- /dev/null +++ b/runtime/internal/lib/os/dir.go @@ -0,0 +1,180 @@ +package os + +import ( + "io/fs" + "sort" + origSyscall "syscall" + "unsafe" + + c "github.com/goplus/llgo/runtime/internal/clite" + "github.com/goplus/llgo/runtime/internal/clite/os" + "github.com/goplus/llgo/runtime/internal/lib/internal/bytealg" + "github.com/goplus/llgo/runtime/internal/lib/syscall" +) + +type readdirMode int + +const ( + readdirName readdirMode = iota + readdirDirEntry + readdirFileInfo +) + +type DirEntry = fs.DirEntry + +func (f *File) Readdirnames(n int) (names []string, err error) { + if f == nil { + return nil, ErrInvalid + } + + entries, err := f.ReadDir(n) + if err != nil { + return nil, err + } + names = make([]string, len(entries)) + for i, entry := range entries { + names[i] = entry.Name() + } + return names, err +} + +func open(path string, flag int, perm uint32) (int, error) { + fd, err := syscall.Open(path, flag, perm) + return fd, err +} + +func openDirNolog(name string) (*File, error) { + var ( + r int + e error + ) + ignoringEINTR(func() error { + r, e = open(name, O_RDONLY|origSyscall.O_CLOEXEC, 0) + return e + }) + if e != nil { + return nil, &PathError{Op: "open", Path: name, Err: e} + } + + if !supportsCloseOnExec { + origSyscall.CloseOnExec(r) + } + + f := newFile(r, name, kindNoPoll) + return f, nil +} + +func openDir(name string) (*File, error) { + return openDirNolog(name) +} + +func ReadDir(name string) ([]DirEntry, error) { + f, err := openDir(name) + if err != nil { + return nil, err + } + defer f.Close() + + dirs, err := f.ReadDir(-1) + sort.Slice(dirs, func(i, j int) bool { + return bytealg.CompareString(dirs[i].Name(), dirs[j].Name()) < 0 + }) + return dirs, err +} + +//go:linkname c_fdopendir C.fdopendir +func c_fdopendir(fd c.Int) uintptr + +func fdopendir(fd int) (dir uintptr, err error) { + return c_fdopendir(c.Int(fd)), nil +} + +//go:linkname c_closedir C.closedir +func c_closedir(dir uintptr) c.Int + +func closedir(dir uintptr) error { + if c_closedir(dir) != 0 { + return syscall.Errno(os.Errno()) + } + return nil +} + +//go:linkname c_readdir C.readdir +func c_readdir(dir uintptr) ([]syscall.Dirent, error) + +func readdir(dir uintptr) ([]syscall.Dirent, error) { + return c_readdir(dir) +} + +func (f *File) ReadDir(n int) (dirents []DirEntry, err error) { + if f == nil { + return nil, ErrInvalid + } + + // Open directory using file descriptor + dir, err := fdopendir(int(f.fd)) + if err != nil { + return nil, err + } + defer closedir(dir) + + // Match Readdir and Readdirnames: don't return nil slices. + dirents = []DirEntry{} + + // Read directory entries + for n < 0 || len(dirents) < n { + entries, err := readdir(dir) + if err != nil { + return dirents, err + } + if len(entries) == 0 { + break + } + + for _, entry := range entries { + // Convert syscall.Dirent to fs.DirEntry + name := bytesToString((*[1024]byte)(unsafe.Pointer(&entry.Name[0]))[:]) + if name == "." || name == ".." { + continue + } + + typ := fs.FileMode(0) + switch entry.Type { + case origSyscall.DT_REG: + typ = 0 + case origSyscall.DT_DIR: + typ = fs.ModeDir + case origSyscall.DT_LNK: + typ = fs.ModeSymlink + case origSyscall.DT_SOCK: + typ = fs.ModeSocket + case origSyscall.DT_FIFO: + typ = fs.ModeNamedPipe + case origSyscall.DT_CHR: + typ = fs.ModeCharDevice + case origSyscall.DT_BLK: + typ = fs.ModeDevice + } + + dirents = append(dirents, &unixDirent{ + parent: f.name, + name: name, + typ: typ, + }) + + if n > 0 && len(dirents) >= n { + break + } + } + } + + return dirents, nil +} + +// bytesToString converts byte slice to string without allocation. +func bytesToString(b []byte) string { + var i int + for i = 0; i < len(b) && b[i] != 0; i++ { + } + return string(b[0:i]) +} diff --git a/runtime/internal/lib/os/exec/exec.go b/runtime/internal/lib/os/exec/exec.go index c884f259..b350d91d 100644 --- a/runtime/internal/lib/os/exec/exec.go +++ b/runtime/internal/lib/os/exec/exec.go @@ -16,7 +16,6 @@ package exec -// llgo:skipall import ( "bytes" "context" @@ -33,6 +32,9 @@ import ( "github.com/goplus/llgo/runtime/internal/lib/internal/syscall/execenv" ) +// llgo:skipall +type _exec struct{} + // Error is returned by LookPath when it fails to classify a file as an // executable. type Error struct { diff --git a/runtime/internal/lib/os/exec_posix.go b/runtime/internal/lib/os/exec_posix.go index 17e473e0..0b9e6731 100644 --- a/runtime/internal/lib/os/exec_posix.go +++ b/runtime/internal/lib/os/exec_posix.go @@ -59,8 +59,12 @@ func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err e // runtime.KeepAlive(attr) if e != nil { - return nil, &PathError{Op: "fork/exec", Path: name, Err: e} + // TODO(lijie): workaround with type assertion + if r, ok := e.(syscall.Errno); !ok || r != 0 { + return nil, &PathError{Op: "fork/exec", Path: name, Err: e} + } } + println("StartProcess", pid, h, e) return newProcess(pid, h), nil } diff --git a/runtime/internal/lib/os/os.go b/runtime/internal/lib/os/os.go index d13938ac..b4c6bf1a 100644 --- a/runtime/internal/lib/os/os.go +++ b/runtime/internal/lib/os/os.go @@ -16,7 +16,6 @@ package os -// llgo:skipall import ( "errors" "runtime" @@ -27,6 +26,9 @@ import ( "github.com/goplus/llgo/runtime/internal/clite/os" ) +// llgo:skipall +type _os struct{} + const ( LLGoPackage = true ) diff --git a/runtime/internal/lib/os/path.go b/runtime/internal/lib/os/path.go index aee2266c..2aa6416a 100644 --- a/runtime/internal/lib/os/path.go +++ b/runtime/internal/lib/os/path.go @@ -53,3 +53,7 @@ func MkdirAll(path string, perm FileMode) error { } return nil } + +func RemoveAll(path string) error { + return removeAll(path) +} diff --git a/runtime/internal/lib/os/removeall_at.go b/runtime/internal/lib/os/removeall_at.go new file mode 100644 index 00000000..40802d58 --- /dev/null +++ b/runtime/internal/lib/os/removeall_at.go @@ -0,0 +1,222 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build unix + +package os + +import ( + "io" + "syscall" + origSyscall "syscall" + "unsafe" + + c "github.com/goplus/llgo/runtime/internal/clite" + "github.com/goplus/llgo/runtime/internal/clite/os" + "github.com/goplus/llgo/runtime/internal/lib/internal/syscall/unix" +) + +func removeAll(path string) error { + if path == "" { + // fail silently to retain compatibility with previous behavior + // of RemoveAll. See issue 28830. + return nil + } + + // The rmdir system call does not permit removing ".", + // so we don't permit it either. + if endsWithDot(path) { + return &PathError{Op: "RemoveAll", Path: path, Err: origSyscall.EINVAL} + } + + // Simple case: if Remove works, we're done. + err := Remove(path) + if err == nil || IsNotExist(err) { + return nil + } + + // RemoveAll recurses by deleting the path base from + // its parent directory + parentDir, base := splitPath(path) + + parent, err := Open(parentDir) + if IsNotExist(err) { + // If parent does not exist, base cannot exist. Fail silently + return nil + } + if err != nil { + return err + } + defer parent.Close() + + if err := removeAllFrom(parent, base); err != nil { + if pathErr, ok := err.(*PathError); ok { + pathErr.Path = parentDir + string(PathSeparator) + pathErr.Path + err = pathErr + } + return err + } + return nil +} + +func removeAllFrom(parent *File, base string) error { + p, err := syscall.BytePtrFromString(base) + if err != nil { + return err + } + parentFd := int(parent.Fd()) + // Simple case: if Unlink (aka remove) works, we're done. + err = ignoringEINTR(func() error { + if os.Unlinkat(c.Int(parentFd), (*c.Char)(unsafe.Pointer(p)), 0) < 0 { + return origSyscall.Errno(os.Errno()) + } + return nil + }) + if err == nil || IsNotExist(err) { + return nil + } + + // EISDIR means that we have a directory, and we need to + // remove its contents. + // EPERM or EACCES means that we don't have write permission on + // the parent directory, but this entry might still be a directory + // whose contents need to be removed. + // Otherwise just return the error. + if err != origSyscall.EISDIR && err != origSyscall.EPERM && err != origSyscall.EACCES { + return &PathError{Op: "unlinkat", Path: base, Err: err} + } + uErr := err + + // Remove the directory's entries. + var recurseErr error + for { + const reqSize = 1024 + var respSize int + + // Open the directory to recurse into + file, err := openDirAt(parentFd, base) + if err != nil { + if IsNotExist(err) { + return nil + } + if err == origSyscall.ENOTDIR || err == unix.NoFollowErrno { + // Not a directory; return the error from the unix.Unlinkat. + return &PathError{Op: "unlinkat", Path: base, Err: uErr} + } + recurseErr = &PathError{Op: "openfdat", Path: base, Err: err} + break + } + + for { + numErr := 0 + + names, readErr := file.Readdirnames(reqSize) + // Errors other than EOF should stop us from continuing. + if readErr != nil && readErr != io.EOF { + file.Close() + if IsNotExist(readErr) { + return nil + } + return &PathError{Op: "readdirnames", Path: base, Err: readErr} + } + + respSize = len(names) + for _, name := range names { + err := removeAllFrom(file, name) + if err != nil { + if pathErr, ok := err.(*PathError); ok { + pathErr.Path = base + string(PathSeparator) + pathErr.Path + } + numErr++ + if recurseErr == nil { + recurseErr = err + } + } + } + + // If we can delete any entry, break to start new iteration. + // Otherwise, we discard current names, get next entries and try deleting them. + if numErr != reqSize { + break + } + } + + // Removing files from the directory may have caused + // the OS to reshuffle it. Simply calling Readdirnames + // again may skip some entries. The only reliable way + // to avoid this is to close and re-open the + // directory. See issue 20841. + file.Close() + + // Finish when the end of the directory is reached + if respSize < reqSize { + break + } + } + + // Remove the directory itself. + unlinkError := ignoringEINTR(func() error { + if os.Unlinkat(c.Int(parentFd), (*c.Char)(unsafe.Pointer(p)), unix.AT_REMOVEDIR) < 0 { + return origSyscall.Errno(os.Errno()) + } + return nil + }) + if unlinkError == nil || IsNotExist(unlinkError) { + return nil + } + + if recurseErr != nil { + return recurseErr + } + return &PathError{Op: "unlinkat", Path: base, Err: unlinkError} +} + +// openDirAt opens a directory name relative to the directory referred to by +// the file descriptor dirfd. If name is anything but a directory (this +// includes a symlink to one), it should return an error. Other than that this +// should act like openFileNolog. +// +// This acts like openFileNolog rather than OpenFile because +// we are going to (try to) remove the file. +// The contents of this file are not relevant for test caching. +func openDirAt(dirfd int, name string) (*File, error) { + p, err := syscall.BytePtrFromString(name) + if err != nil { + return nil, err + } + var r int + for { + var e error + r = int(os.Openat(c.Int(dirfd), (*c.Char)(unsafe.Pointer(p)), origSyscall.O_RDONLY|origSyscall.O_CLOEXEC|origSyscall.O_DIRECTORY|origSyscall.O_NOFOLLOW, 0)) + if r >= 0 { + break + } + e = origSyscall.Errno(r) + + // See comment in openFileNolog. + if e == origSyscall.EINTR { + continue + } + + return nil, e + } + + if !supportsCloseOnExec { + origSyscall.CloseOnExec(r) + } + + // We use kindNoPoll because we know that this is a directory. + return newFile(r, name, kindNoPoll), nil +} + +// endsWithDot reports whether the final component of path is ".". +func endsWithDot(path string) bool { + if path == "." { + return true + } + if len(path) >= 2 && path[len(path)-1] == '.' && IsPathSeparator(path[len(path)-2]) { + return true + } + return false +} diff --git a/runtime/internal/lib/os/removeall_noat.go b/runtime/internal/lib/os/removeall_noat.go new file mode 100644 index 00000000..2b8a7727 --- /dev/null +++ b/runtime/internal/lib/os/removeall_noat.go @@ -0,0 +1,142 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !unix + +package os + +import ( + "io" + "runtime" + "syscall" +) + +func removeAll(path string) error { + if path == "" { + // fail silently to retain compatibility with previous behavior + // of RemoveAll. See issue 28830. + return nil + } + + // The rmdir system call permits removing "." on Plan 9, + // so we don't permit it to remain consistent with the + // "at" implementation of RemoveAll. + if endsWithDot(path) { + return &PathError{Op: "RemoveAll", Path: path, Err: syscall.EINVAL} + } + + // Simple case: if Remove works, we're done. + err := Remove(path) + if err == nil || IsNotExist(err) { + return nil + } + + // Otherwise, is this a directory we need to recurse into? + dir, serr := Lstat(path) + if serr != nil { + if serr, ok := serr.(*PathError); ok && (IsNotExist(serr.Err) || serr.Err == syscall.ENOTDIR) { + return nil + } + return serr + } + if !dir.IsDir() { + // Not a directory; return the error from Remove. + return err + } + + // Remove contents & return first error. + err = nil + for { + fd, err := Open(path) + if err != nil { + if IsNotExist(err) { + // Already deleted by someone else. + return nil + } + return err + } + + const reqSize = 1024 + var names []string + var readErr error + + for { + numErr := 0 + names, readErr = fd.Readdirnames(reqSize) + + for _, name := range names { + err1 := RemoveAll(path + string(PathSeparator) + name) + if err == nil { + err = err1 + } + if err1 != nil { + numErr++ + } + } + + // If we can delete any entry, break to start new iteration. + // Otherwise, we discard current names, get next entries and try deleting them. + if numErr != reqSize { + break + } + } + + // Removing files from the directory may have caused + // the OS to reshuffle it. Simply calling Readdirnames + // again may skip some entries. The only reliable way + // to avoid this is to close and re-open the + // directory. See issue 20841. + fd.Close() + + if readErr == io.EOF { + break + } + // If Readdirnames returned an error, use it. + if err == nil { + err = readErr + } + if len(names) == 0 { + break + } + + // We don't want to re-open unnecessarily, so if we + // got fewer than request names from Readdirnames, try + // simply removing the directory now. If that + // succeeds, we are done. + if len(names) < reqSize { + err1 := Remove(path) + if err1 == nil || IsNotExist(err1) { + return nil + } + + if err != nil { + // We got some error removing the + // directory contents, and since we + // read fewer names than we requested + // there probably aren't more files to + // remove. Don't loop around to read + // the directory again. We'll probably + // just get the same error. + return err + } + } + } + + // Remove directory. + err1 := Remove(path) + if err1 == nil || IsNotExist(err1) { + return nil + } + if runtime.GOOS == "windows" && IsPermission(err1) { + if fs, err := Stat(path); err == nil { + if err = Chmod(path, FileMode(0200|int(fs.Mode()))); err == nil { + err1 = Remove(path) + } + } + } + if err == nil { + err = err1 + } + return err +} diff --git a/runtime/internal/lib/os/signal/signal.go b/runtime/internal/lib/os/signal/signal.go new file mode 100644 index 00000000..6d2719ff --- /dev/null +++ b/runtime/internal/lib/os/signal/signal.go @@ -0,0 +1,25 @@ +package signal + +func signal_disable(uint32) { + panic("signal_disable not implemented") +} + +func signal_enable(uint32) { + panic("signal_enable not implemented") +} + +func signal_ignore(uint32) { + panic("signal_ignore not implemented") +} + +func signal_ignored(uint32) bool { + panic("signal_ignored not implemented") +} + +func signal_recv() uint32 { + panic("signal_recv not implemented") +} + +func signalWaitUntilIdle() { + panic("signalWaitUntilIdle not implemented") +} diff --git a/runtime/internal/lib/os/types_unix.go b/runtime/internal/lib/os/types_unix.go index 12f79b21..e0aeae1f 100644 --- a/runtime/internal/lib/os/types_unix.go +++ b/runtime/internal/lib/os/types_unix.go @@ -9,7 +9,7 @@ package os import ( "time" - "github.com/goplus/llgo/runtime/internal/lib/syscall" + "github.com/goplus/llgo/runtime/internal/clite/syscall" ) // A fileStat is the implementation of FileInfo returned by Stat and Lstat. diff --git a/runtime/internal/lib/runtime/debug/debug.go b/runtime/internal/lib/runtime/debug/debug.go new file mode 100644 index 00000000..2c1ae0bc --- /dev/null +++ b/runtime/internal/lib/runtime/debug/debug.go @@ -0,0 +1,5 @@ +package debug + +func SetTraceback(level string) { + panic("todo: runtime/debug.SetTraceback") +} diff --git a/runtime/internal/lib/runtime/extern.go b/runtime/internal/lib/runtime/extern.go index 07b91ef1..8f839a48 100644 --- a/runtime/internal/lib/runtime/extern.go +++ b/runtime/internal/lib/runtime/extern.go @@ -7,3 +7,7 @@ package runtime func Caller(skip int) (pc uintptr, file string, line int, ok bool) { panic("todo: runtime.Caller") } + +func Callers(skip int, pc []uintptr) int { + panic("todo: runtime.Callers") +} diff --git a/runtime/internal/lib/runtime/internal/syscall/syscall.go b/runtime/internal/lib/runtime/internal/syscall/syscall.go new file mode 100644 index 00000000..d461060c --- /dev/null +++ b/runtime/internal/lib/runtime/internal/syscall/syscall.go @@ -0,0 +1,6 @@ +package syscall + +import _ "unsafe" + +// llgo:skipall +type _syscall6 struct{} diff --git a/runtime/internal/lib/runtime/pprof/pprof.go b/runtime/internal/lib/runtime/pprof/pprof.go new file mode 100644 index 00000000..7c77944c --- /dev/null +++ b/runtime/internal/lib/runtime/pprof/pprof.go @@ -0,0 +1,24 @@ +package pprof + +import ( + "io" +) + +// llgo:skipall +type Profile struct{} + +func (p *Profile) WriteTo(w io.Writer, verbose bool) (int, error) { + panic("WriteTo not implemented") +} + +func StartCPUProfile(w io.Writer) error { + panic("StartCPUProfile not implemented") +} + +func StopCPUProfile() { + panic("StopCPUProfile not implemented") +} + +func Lookup(name string) *Profile { + panic("Lookup not implemented") +} diff --git a/runtime/internal/lib/runtime/runtime.go b/runtime/internal/lib/runtime/runtime.go index 9e3edd75..75a292c5 100644 --- a/runtime/internal/lib/runtime/runtime.go +++ b/runtime/internal/lib/runtime/runtime.go @@ -16,8 +16,22 @@ package runtime +/* +#include + +int llgo_maxprocs() { + #ifdef _SC_NPROCESSORS_ONLN + return (int)sysconf(_SC_NPROCESSORS_ONLN); + #else + return 1; + #endif +} +*/ +import "C" import ( - _ "unsafe" + "unsafe" + + "github.com/goplus/llgo/runtime/internal/clite/pthread" ) // llgo:skipall @@ -27,12 +41,23 @@ type _runtime struct{} // GOROOT environment variable, if set at process start, // or else the root used during the Go build. func GOROOT() string { - /* - s := gogetenv("GOROOT") - if s != "" { - return s - } - return defaultGOROOT - */ - panic("todo: GOROOT") + return "" +} + +//go:linkname c_maxprocs C.llgo_maxprocs +func c_maxprocs() int32 + +func GOMAXPROCS(n int) int { + return int(c_maxprocs()) +} + +func Goexit() { + pthread.Exit(nil) +} + +func KeepAlive(x any) { +} + +func write(fd uintptr, p unsafe.Pointer, n int32) int32 { + return int32(C.write(C.int(fd), p, C.size_t(n))) } diff --git a/runtime/internal/lib/runtime/runtime2.go b/runtime/internal/lib/runtime/runtime2.go index f8c1b925..8b30b526 100644 --- a/runtime/internal/lib/runtime/runtime2.go +++ b/runtime/internal/lib/runtime/runtime2.go @@ -4,6 +4,10 @@ package runtime +import ( + "runtime" +) + // Layout of in-memory per-function information prepared by linker // See https://golang.org/s/go12symtab. // Keep in sync with linker (../cmd/link/internal/ld/pcln.go:/pclntab) @@ -11,3 +15,33 @@ package runtime type _func struct { unused [8]byte } + +func Stack(buf []byte, all bool) int { + panic("todo: runtime.Stack") +} + +func StartTrace() error { + panic("todo: runtime.StartTrace") +} + +func ReadTrace() []byte { + panic("todo: runtime.ReadTrace") +} + +func StopTrace() { + panic("todo: runtime.StopTrace") +} + +func ReadMemStats(m *runtime.MemStats) { + panic("todo: runtime.ReadMemStats") +} + +func SetMutexProfileFraction(rate int) int { + panic("todo: runtime.SetMutexProfileFraction") +} + +func SetBlockProfileRate(rate int) { + panic("todo: runtime.SetBlockProfileRate") +} + +var MemProfileRate int = 512 * 1024 diff --git a/runtime/internal/lib/runtime/runtime_gc.go b/runtime/internal/lib/runtime/runtime_gc.go new file mode 100644 index 00000000..a80d00b9 --- /dev/null +++ b/runtime/internal/lib/runtime/runtime_gc.go @@ -0,0 +1,9 @@ +//go:build !nogc + +package runtime + +import "github.com/goplus/llgo/runtime/internal/clite/bdwgc" + +func GC() { + bdwgc.Gcollect() +} diff --git a/runtime/internal/lib/runtime/runtime_nogc.go b/runtime/internal/lib/runtime/runtime_nogc.go new file mode 100644 index 00000000..3f114260 --- /dev/null +++ b/runtime/internal/lib/runtime/runtime_nogc.go @@ -0,0 +1,7 @@ +//go:build nogc + +package runtime + +func GC() { + +} diff --git a/runtime/internal/lib/runtime/trace/trace.go b/runtime/internal/lib/runtime/trace/trace.go new file mode 100644 index 00000000..cbb937ad --- /dev/null +++ b/runtime/internal/lib/runtime/trace/trace.go @@ -0,0 +1,5 @@ +package trace + +func userTaskEnd(id uint64) { + panic("todo: runtime/trace.userTaskEnd") +} diff --git a/runtime/internal/lib/sync/atomic/type.go b/runtime/internal/lib/sync/atomic/type.go new file mode 100644 index 00000000..d9e3c9ca --- /dev/null +++ b/runtime/internal/lib/sync/atomic/type.go @@ -0,0 +1,103 @@ +package atomic + +// An Int64 is an atomic int64. The zero value is zero. +type Int64 struct { + _ noCopy + _ align64 + v int64 +} + +// Load atomically loads and returns the value stored in x. +func (x *Int64) Load() int64 { return LoadInt64(&x.v) } + +// Store atomically stores val into x. +func (x *Int64) Store(val int64) { StoreInt64(&x.v, val) } + +// Swap atomically stores new into x and returns the previous value. +func (x *Int64) Swap(new int64) (old int64) { return SwapInt64(&x.v, new) } + +// CompareAndSwap executes the compare-and-swap operation for x. +func (x *Int64) CompareAndSwap(old, new int64) (swapped bool) { + return CompareAndSwapInt64(&x.v, old, new) +} + +// Add atomically adds delta to x and returns the new value. +func (x *Int64) Add(delta int64) (new int64) { return AddInt64(&x.v, delta) } + +// And atomically performs a bitwise AND operation on x using the bitmask +// provided as mask and returns the old value. +func (x *Int64) And(mask int64) (old int64) { return AndInt64(&x.v, mask) } + +// Or atomically performs a bitwise OR operation on x using the bitmask +// provided as mask and returns the old value. +func (x *Int64) Or(mask int64) (old int64) { return OrInt64(&x.v, mask) } + +// A Uint64 is an atomic uint64. The zero value is zero. +type Uint64 struct { + _ noCopy + _ align64 + v uint64 +} + +// Load atomically loads and returns the value stored in x. +func (x *Uint64) Load() uint64 { return LoadUint64(&x.v) } + +// Store atomically stores val into x. +func (x *Uint64) Store(val uint64) { StoreUint64(&x.v, val) } + +// Swap atomically stores new into x and returns the previous value. +func (x *Uint64) Swap(new uint64) (old uint64) { return SwapUint64(&x.v, new) } + +// CompareAndSwap executes the compare-and-swap operation for x. +func (x *Uint64) CompareAndSwap(old, new uint64) (swapped bool) { + return CompareAndSwapUint64(&x.v, old, new) +} + +// Add atomically adds delta to x and returns the new value. +func (x *Uint64) Add(delta uint64) (new uint64) { return AddUint64(&x.v, delta) } + +// And atomically performs a bitwise AND operation on x using the bitmask +// provided as mask and returns the old value. +func (x *Uint64) And(mask uint64) (old uint64) { return AndUint64(&x.v, mask) } + +// Or atomically performs a bitwise OR operation on x using the bitmask +// provided as mask and returns the old value. +func (x *Uint64) Or(mask uint64) (old uint64) { return OrUint64(&x.v, mask) } + +// noCopy may be added to structs which must not be copied +// after the first use. +// +// See https://golang.org/issues/8005#issuecomment-190753527 +// for details. +// +// Note that it must not be embedded, due to the Lock and Unlock methods. +type noCopy struct{} + +// Lock is a no-op used by -copylocks checker from `go vet`. +func (*noCopy) Lock() {} +func (*noCopy) Unlock() {} + +// align64 may be added to structs that must be 64-bit aligned. +// This struct is recognized by a special case in the compiler +// and will not work if copied to any other package. +type align64 struct{} + +// llgo:link AndInt64 llgo.atomicAnd +func AndInt64(addr *int64, mask int64) (old int64) { + panic("implement by llgo instruction") +} + +// llgo:link AndUint64 llgo.atomicAnd +func AndUint64(addr *uint64, mask uint64) (old uint64) { + panic("implement by llgo instruction") +} + +// llgo:link OrInt64 llgo.atomicOr +func OrInt64(addr *int64, mask int64) (old int64) { + panic("implement by llgo instruction") +} + +// llgo:link OrUint64 llgo.atomicOr +func OrUint64(addr *uint64, mask uint64) (old uint64) { + panic("implement by llgo instruction") +} diff --git a/runtime/internal/lib/sync/sync.go b/runtime/internal/lib/sync/sync.go index 1769f441..e7c250be 100644 --- a/runtime/internal/lib/sync/sync.go +++ b/runtime/internal/lib/sync/sync.go @@ -16,7 +16,6 @@ package sync -// llgo:skipall import ( gosync "sync" "unsafe" @@ -25,6 +24,9 @@ import ( "github.com/goplus/llgo/runtime/internal/clite/pthread/sync" ) +// llgo:skipall +type _sync struct{} + // ----------------------------------------------------------------------------- type Mutex sync.Mutex diff --git a/runtime/internal/lib/syscall/syscall.go b/runtime/internal/lib/syscall/syscall.go index c87cff44..c5fac4b7 100644 --- a/runtime/internal/lib/syscall/syscall.go +++ b/runtime/internal/lib/syscall/syscall.go @@ -17,16 +17,20 @@ package syscall import ( + origSyscall "syscall" "unsafe" c "github.com/goplus/llgo/runtime/internal/clite" "github.com/goplus/llgo/runtime/internal/clite/os" "github.com/goplus/llgo/runtime/internal/clite/syscall" + "github.com/goplus/llgo/runtime/internal/lib/internal/bytealg" ) // llgo:skipall type _syscall struct{} +type Iovec syscall.Iovec + type Timespec syscall.Timespec type Timeval syscall.Timeval @@ -192,3 +196,24 @@ func setrlimit(which int, lim *Rlimit) (err error) { } return Errno(ret) } + +func BytePtrFromString(s string) (*byte, error) { + a, err := ByteSliceFromString(s) + if err != nil { + return nil, err + } + return &a[0], nil +} + +func ByteSliceFromString(s string) ([]byte, error) { + if bytealg.IndexByteString(s, 0) != -1 { + return nil, Errno(syscall.EINVAL) + } + a := make([]byte, len(s)+1) + copy(a, s) + return a, nil +} + +func Accept(fd int) (nfd int, sa origSyscall.Sockaddr, err error) { + panic("todo: syscall.Accept") +} diff --git a/runtime/internal/lib/syscall/syscall_darwin.go b/runtime/internal/lib/syscall/syscall_darwin.go new file mode 100644 index 00000000..9588a99c --- /dev/null +++ b/runtime/internal/lib/syscall/syscall_darwin.go @@ -0,0 +1,5 @@ +package syscall + +func Sysctl(key string) (string, error) { + panic("todo: syscall.Sysctl") +} diff --git a/runtime/internal/lib/syscall/syscall_linux.go b/runtime/internal/lib/syscall/syscall_linux.go index 36230cad..99e01f10 100644 --- a/runtime/internal/lib/syscall/syscall_linux.go +++ b/runtime/internal/lib/syscall/syscall_linux.go @@ -12,6 +12,7 @@ package syscall import ( + origSyscall "syscall" _ "unsafe" c "github.com/goplus/llgo/runtime/internal/clite" @@ -126,3 +127,11 @@ func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) { func faccessat(dirfd c.Int, path *c.Char, mode c.Int, flags c.Int) c.Int // ----------------------------------------------------------------------------- + +func Accept4(fd int, flags int) (nfd int, sa origSyscall.Sockaddr, err error) { + panic("todo: syscall.Accept4") +} + +func Uname(buf *origSyscall.Utsname) (err error) { + panic("todo: syscall.Uname") +} diff --git a/runtime/internal/lib/syscall/syscall_unix.go b/runtime/internal/lib/syscall/syscall_unix.go index a0b16b79..c81c214c 100644 --- a/runtime/internal/lib/syscall/syscall_unix.go +++ b/runtime/internal/lib/syscall/syscall_unix.go @@ -22,6 +22,8 @@ var ( type Errno uintptr +type Dirent = syscall.Dirent + func (e Errno) Error() string { ret := c.Strerror(c.Int(e)) return unsafe.String((*byte)(unsafe.Pointer(ret)), c.Strlen(ret)) @@ -69,3 +71,11 @@ func (s Signal) String() string { */ panic("todo: syscall.Signal.String") } + +func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) { + panic("todo: syscall.Mmap") +} + +func Munmap(b []byte) (err error) { + panic("todo: syscall.Munmap") +} diff --git a/runtime/internal/lib/time/sleep.go b/runtime/internal/lib/time/sleep.go index c227ac5f..3f5914c2 100644 --- a/runtime/internal/lib/time/sleep.go +++ b/runtime/internal/lib/time/sleep.go @@ -4,23 +4,27 @@ package time +import ( + "sync" + "unsafe" + + c "github.com/goplus/llgo/runtime/internal/clite" + "github.com/goplus/llgo/runtime/internal/clite/libuv" +) + // Sleep pauses the current goroutine for at least the duration d. // A negative or zero duration causes Sleep to return immediately. func Sleep(d Duration) { - panic("todo: time.Sleep") + c.Usleep(c.Uint(d.Nanoseconds())) } // Interface to timers implemented in package runtime. // Must be in sync with ../runtime/time.go:/^type timer type runtimeTimer struct { - pp uintptr - when int64 - period int64 - f func(any, uintptr) // NOTE: must not be closure - arg any - seq uintptr - nextwhen int64 - status uint32 + libuv.Timer + when int64 + f func(any, uintptr) + arg any } // when is a helper function for setting the 'when' field of a runtimeTimer. @@ -40,10 +44,6 @@ func when(d Duration) int64 { return t } -func startTimer(*runtimeTimer) { panic("todo: time.startTimer") } -func stopTimer(*runtimeTimer) bool { panic("todo: time.stopTimer") } -func resetTimer(*runtimeTimer, int64) bool { panic("todo: time.resetTimer") } - /* TODO(xsw): func modTimer(t *runtimeTimer, when, period int64, f func(any, uintptr), arg any, seq uintptr) { panic("todo: time.modTimer") @@ -82,9 +82,6 @@ type Timer struct { // If the caller needs to know whether f is completed, it must coordinate // with f explicitly. func (t *Timer) Stop() bool { - if t.r.f == nil { - panic("time: Stop called on uninitialized Timer") - } return stopTimer(&t.r) } @@ -139,9 +136,6 @@ func NewTimer(d Duration) *Timer { // one. If the caller needs to know whether the prior execution of // f is completed, it must coordinate with f explicitly. func (t *Timer) Reset(d Duration) bool { - if t.r.f == nil { - panic("time: Reset called on uninitialized Timer") - } w := when(d) return resetTimer(&t.r, w) } @@ -182,3 +176,64 @@ func AfterFunc(d Duration, f func()) *Timer { func goFunc(arg any, seq uintptr) { go arg.(func())() } + +var ( + timerLoop *libuv.Loop + timerOnce sync.Once +) + +func init() { + timerOnce.Do(func() { + timerLoop = libuv.LoopNew() + }) + go func() { + timerLoop.Run(libuv.RUN_DEFAULT) + }() +} + +// cross thread +func timerEvent(async *libuv.Async) { + a := (*asyncTimerEvent)(unsafe.Pointer(async)) + a.cb() + a.Close(nil) +} + +type asyncTimerEvent struct { + libuv.Async + cb func() +} + +func timerCallback(t *libuv.Timer) { +} + +func startTimer(r *runtimeTimer) { + asyncTimer := &asyncTimerEvent{ + cb: func() { + libuv.InitTimer(timerLoop, &r.Timer) + r.Start(timerCallback, uint64(r.when), 0) + }, + } + timerLoop.Async(&asyncTimer.Async, timerEvent) + asyncTimer.Send() +} + +func stopTimer(r *runtimeTimer) bool { + asyncTimer := &asyncTimerEvent{ + cb: func() { + r.Stop() + }, + } + timerLoop.Async(&asyncTimer.Async, timerEvent) + return asyncTimer.Send() == 0 +} + +func resetTimer(r *runtimeTimer, when int64) bool { + asyncTimer := &asyncTimerEvent{ + cb: func() { + r.Stop() + r.Start(timerCallback, uint64(when), 0) + }, + } + timerLoop.Async(&asyncTimer.Async, timerEvent) + return asyncTimer.Send() == 0 +} diff --git a/runtime/internal/lib/time/tick.go b/runtime/internal/lib/time/tick.go new file mode 100644 index 00000000..bfbb0f67 --- /dev/null +++ b/runtime/internal/lib/time/tick.go @@ -0,0 +1,36 @@ +package time + +import "unsafe" + +type Ticker struct { + C <-chan Time // The channel on which the ticks are delivered. +} + +func NewTicker(d Duration) *Ticker { + if d <= 0 { + panic("non-positive interval for NewTicker") + } + c := make(chan Time, 1) + t := &Timer{C: c} + t.C = c + startTimer(&t.r) + return (*Ticker)(unsafe.Pointer(t)) +} + +func (t *Ticker) Stop() { + stopTimer(&(*Timer)(unsafe.Pointer(t)).r) +} + +func (t *Ticker) Reset(d Duration) { + if d <= 0 { + panic("non-positive interval for Ticker.Reset") + } + resetTimer(&(*Timer)(unsafe.Pointer(t)).r, when(d)) +} + +func Tick(d Duration) <-chan Time { + if d <= 0 { + return nil + } + return NewTicker(d).C +} diff --git a/runtime/internal/lib/time/time.go b/runtime/internal/lib/time/time.go index d7db0d46..17b97b5d 100644 --- a/runtime/internal/lib/time/time.go +++ b/runtime/internal/lib/time/time.go @@ -187,6 +187,10 @@ func (t Time) Compare(u Time) int { return 0 } +func (t Time) UnixNano() int64 { + return (t.unixSec())*1e9 + int64(t.nsec()) +} + // Equal reports whether t and u represent the same time instant. // Two times can be equal even if they are in different locations. // For example, 6:00 +0200 and 4:00 UTC are Equal.