Compare commits
99 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee0e0a5c51 | ||
|
|
43fd5d233a | ||
|
|
0bd39ed035 | ||
|
|
7b2747ce0c | ||
|
|
1db8aad039 | ||
|
|
fb2d4267f5 | ||
|
|
d7b203ae08 | ||
|
|
578bc165c4 | ||
|
|
a1d46e905b | ||
|
|
9102ca6b1e | ||
|
|
3e07f2e3bc | ||
|
|
94cf6f6640 | ||
|
|
6da5fe4317 | ||
|
|
835d6fee1e | ||
|
|
3a68dee850 | ||
|
|
294abb5126 | ||
|
|
2ccfa6a2e8 | ||
|
|
5fca8ebd4e | ||
|
|
d3df782fca | ||
|
|
f7bf671050 | ||
|
|
b04ac8df30 | ||
|
|
bb03df7059 | ||
|
|
98072f3f4b | ||
|
|
3bf0780a67 | ||
|
|
4bff9cc3df | ||
|
|
13c68a0184 | ||
|
|
a1fdc05e26 | ||
|
|
6d92949715 | ||
|
|
93bff83e3d | ||
|
|
5cf31bd3f3 | ||
|
|
929d4c8d61 | ||
|
|
482f796bad | ||
|
|
d85f532ab1 | ||
|
|
b1654f7807 | ||
|
|
4f8526e527 | ||
|
|
e470bc2069 | ||
|
|
efa771f3ff | ||
|
|
4b568fc469 | ||
|
|
d06146ed97 | ||
|
|
8e0e809733 | ||
|
|
806193fc6e | ||
|
|
cdfa0166bd | ||
|
|
d1f33a6c4c | ||
|
|
b3e1b6fdbf | ||
|
|
0bd259403c | ||
|
|
c186846463 | ||
|
|
5f92c3b3fc | ||
|
|
0665091cef | ||
|
|
688d153427 | ||
|
|
bec5ba7a73 | ||
|
|
acedf4d6a3 | ||
|
|
5dd5494f93 | ||
|
|
f253e4fabe | ||
|
|
acd09d24d5 | ||
|
|
ceac95c81a | ||
|
|
47a05d0ea2 | ||
|
|
d2975479f2 | ||
|
|
3c238ffae7 | ||
|
|
69f8d1b717 | ||
|
|
45cd9e65d3 | ||
|
|
2e4b1d8c2b | ||
|
|
4e3b65188d | ||
|
|
0ab32e066b | ||
|
|
79d8b00b27 | ||
|
|
0687efaec6 | ||
|
|
eb02c5a451 | ||
|
|
85509c777d | ||
|
|
27677f86e4 | ||
|
|
16174ca874 | ||
|
|
a4e9233231 | ||
|
|
4fdfafa17f | ||
|
|
c9a7dab419 | ||
|
|
8882d75132 | ||
|
|
f67b15b926 | ||
|
|
2d7958f726 | ||
|
|
36072584d0 | ||
|
|
2119e52f55 | ||
|
|
ca1aa6b663 | ||
|
|
10af671b76 | ||
|
|
a4ec6cce96 | ||
|
|
5082ba7102 | ||
|
|
7405e7001b | ||
|
|
4c70651b81 | ||
|
|
21b5b60278 | ||
|
|
0abc5ec452 | ||
|
|
b1d2d620fa | ||
|
|
af6e4abe84 | ||
|
|
45b4315842 | ||
|
|
d2cb96a9e5 | ||
|
|
a3ff845a14 | ||
|
|
0d3e78ad94 | ||
|
|
375b2b579e | ||
|
|
a578155dcb | ||
|
|
df5f1afb74 | ||
|
|
b2a2b2f29d | ||
|
|
91df9957f5 | ||
|
|
08e0ace9a2 | ||
|
|
a4286dbd4b | ||
|
|
f2e15a6846 |
49
.github/workflows/go.yml
vendored
49
.github/workflows/go.yml
vendored
@@ -26,18 +26,47 @@ jobs:
|
||||
if: startsWith(matrix.os, 'macos')
|
||||
run: |
|
||||
brew update
|
||||
brew install llvm@${{ matrix.llvm }} pkg-config bdw-gc openssl cjson sqlite python@3.12
|
||||
brew install llvm@${{matrix.llvm}} pkg-config bdw-gc openssl
|
||||
echo "$(brew --prefix llvm@${{matrix.llvm}})/bin" >> $GITHUB_PATH
|
||||
|
||||
# Install optional deps for demos.
|
||||
#
|
||||
# NOTE: Keep this list updated as new deps are introduced.
|
||||
opt_deps=(
|
||||
cjson # for github.com/goplus/llgo/c/cjson
|
||||
sqlite # for github.com/goplus/llgo/c/sqlite
|
||||
python@3.12 # for github.com/goplus/llgo/py
|
||||
)
|
||||
brew install "${opt_deps[@]}"
|
||||
|
||||
- name: Install dependencies
|
||||
if: startsWith(matrix.os, 'ubuntu')
|
||||
run: |
|
||||
echo "deb http://apt.llvm.org/$(lsb_release -cs)/ llvm-toolchain-$(lsb_release -cs)-${{matrix.llvm}} main" | sudo tee /etc/apt/sources.list.d/llvm.list
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y llvm-${{ matrix.llvm }}-dev clang-${{ matrix.llvm }} lld-${{ matrix.llvm }} pkg-config libgc-dev libssl-dev libcjson-dev libsqlite3-dev python3.12-dev
|
||||
sudo apt-get install -y llvm-${{matrix.llvm}}-dev clang-${{matrix.llvm}} lld-${{matrix.llvm}} pkg-config libgc-dev libssl-dev zlib1g-dev
|
||||
echo "/usr/lib/llvm-${{matrix.llvm}}/bin" >> $GITHUB_PATH
|
||||
|
||||
# Install optional deps for demos.
|
||||
#
|
||||
# NOTE: Keep this list updated as new deps are introduced.
|
||||
opt_deps=(
|
||||
libcjson-dev # for github.com/goplus/llgo/c/cjson
|
||||
libsqlite3-dev # for github.com/goplus/llgo/c/sqlite
|
||||
python3.12-dev # for github.com/goplus/llgo/py
|
||||
)
|
||||
sudo apt-get install -y "${opt_deps[@]}"
|
||||
|
||||
- name: Install further optional dependencies for demos
|
||||
run: |
|
||||
wget -P ./_demo/llama2-c https://huggingface.co/karpathy/tinyllamas/resolve/main/stories15M.bin
|
||||
py_deps=(
|
||||
numpy # for github.com/goplus/llgo/py/numpy
|
||||
torch # for github.com/goplus/llgo/py/torch
|
||||
)
|
||||
pip3 install --break-system-packages "${py_deps[@]}"
|
||||
|
||||
- name: Clang information
|
||||
run: |
|
||||
echo $PATH
|
||||
@@ -53,27 +82,25 @@ jobs:
|
||||
run: go build -v ./...
|
||||
|
||||
- name: Test
|
||||
if: matrix.os != 'macos-latest'
|
||||
if: ${{!startsWith(matrix.os, 'macos')}}
|
||||
run: go test -v ./...
|
||||
|
||||
- name: Test with coverage
|
||||
if: matrix.os == 'macos-latest'
|
||||
if: startsWith(matrix.os, 'macos')
|
||||
run: go test -v -coverprofile="coverage.txt" -covermode=atomic ./...
|
||||
|
||||
- name: Install
|
||||
run: go install ./...
|
||||
|
||||
- name: LLGO tests
|
||||
if: matrix.os != 'ubuntu-latest'
|
||||
if: ${{!startsWith(matrix.os, 'ubuntu')}}
|
||||
run: |
|
||||
echo "Test result on ${{matrix.os}} with LLVM ${{matrix.llvm}}" > result.md
|
||||
LLGOROOT=$PWD bash .github/workflows/test_llgo.sh
|
||||
bash .github/workflows/test_llgo.sh
|
||||
|
||||
- name: Test _demo and _pydemo
|
||||
run: |
|
||||
set +e
|
||||
LLGOROOT=$PWD bash .github/workflows/test_demo.sh
|
||||
exit 0
|
||||
- name: Test demos
|
||||
continue-on-error: true
|
||||
run: bash .github/workflows/test_demo.sh
|
||||
|
||||
- name: Show test result
|
||||
run: cat result.md
|
||||
|
||||
3
.github/workflows/test_demo.sh
vendored
3
.github/workflows/test_demo.sh
vendored
@@ -1,4 +1,5 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# llgo run subdirectories under _demo and _pydemo
|
||||
total=0
|
||||
@@ -8,7 +9,7 @@ for d in ./_demo/* ./_pydemo/*; do
|
||||
total=$((total+1))
|
||||
if [ -d "$d" ]; then
|
||||
echo "Testing $d"
|
||||
if ! llgo run "$d"; then
|
||||
if ! (cd "$d" && llgo run .); then
|
||||
echo "FAIL"
|
||||
failed=$((failed+1))
|
||||
failed_cases="$failed_cases\n* :x: $d"
|
||||
|
||||
2
.github/workflows/test_llgo.sh
vendored
2
.github/workflows/test_llgo.sh
vendored
@@ -1,8 +1,6 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
export LLGOROOT=$PWD
|
||||
|
||||
testcmd=/tmp/test
|
||||
llgo build -o $testcmd ./c/bdwgc/_test
|
||||
cases=$($testcmd)
|
||||
|
||||
@@ -20,8 +20,8 @@ builds:
|
||||
flags:
|
||||
- -tags=darwin,amd64,byollvm
|
||||
ldflags:
|
||||
- -X github.com/goplus/llgo/xtool/env.buildVersion=v{{.Version}}
|
||||
- -X github.com/goplus/llgo/xtool/env.buildDate={{.Date}}
|
||||
- -X github.com/goplus/llgo/x/env.buildVersion=v{{.Version}}
|
||||
- -X github.com/goplus/llgo/x/env.buildTime={{.Date}}
|
||||
- -X github.com/goplus/llgo/xtool/env/llvm.ldLLVMConfigBin=/usr/local/opt/llvm@18/bin/llvm-config
|
||||
env:
|
||||
- CC=o64-clang
|
||||
@@ -36,8 +36,8 @@ builds:
|
||||
flags:
|
||||
- -tags=darwin,arm64,byollvm
|
||||
ldflags:
|
||||
- -X github.com/goplus/llgo/xtool/env.buildVersion=v{{.Version}}
|
||||
- -X github.com/goplus/llgo/xtool/env.buildDate={{.Date}}
|
||||
- -X github.com/goplus/llgo/x/env.buildVersion=v{{.Version}}
|
||||
- -X github.com/goplus/llgo/x/env.buildTime={{.Date}}
|
||||
- -X github.com/goplus/llgo/xtool/env/llvm.ldLLVMConfigBin=/opt/homebrew/opt/llvm@18/bin/llvm-config
|
||||
env:
|
||||
- CC=oa64-clang
|
||||
@@ -52,8 +52,8 @@ builds:
|
||||
flags:
|
||||
- -tags=linux,amd64,byollvm
|
||||
ldflags:
|
||||
- -X github.com/goplus/llgo/xtool/env.buildVersion=v{{.Version}}
|
||||
- -X github.com/goplus/llgo/xtool/env.buildDate={{.Date}}
|
||||
- -X github.com/goplus/llgo/x/env.buildVersion=v{{.Version}}
|
||||
- -X github.com/goplus/llgo/x/env.buildTime={{.Date}}
|
||||
- -X github.com/goplus/llgo/xtool/env/llvm.ldLLVMConfigBin=/usr/lib/llvm-18/bin/llvm-config
|
||||
env:
|
||||
- CC=x86_64-linux-gnu-gcc
|
||||
@@ -68,8 +68,8 @@ builds:
|
||||
flags:
|
||||
- -tags=linux,arm64,byollvm
|
||||
ldflags:
|
||||
- -X github.com/goplus/llgo/xtool/env.buildVersion=v{{.Version}}
|
||||
- -X github.com/goplus/llgo/xtool/env.buildDate={{.Date}}
|
||||
- -X github.com/goplus/llgo/x/env.buildVersion=v{{.Version}}
|
||||
- -X github.com/goplus/llgo/x/env.buildTime={{.Date}}
|
||||
- -X github.com/goplus/llgo/xtool/env/llvm.ldLLVMConfigBin=/usr/lib/llvm-18/bin/llvm-config
|
||||
env:
|
||||
- CC=aarch64-linux-gnu-gcc
|
||||
|
||||
26
README.md
26
README.md
@@ -269,13 +269,16 @@ Here are the Go packages that can be imported correctly:
|
||||
* [unicode/utf8](https://pkg.go.dev/unicode/utf8)
|
||||
* [unicode/utf16](https://pkg.go.dev/unicode/utf16)
|
||||
* [math](https://pkg.go.dev/math)
|
||||
* [math/big](https://pkg.go.dev/math/big) (partially)
|
||||
* [math/bits](https://pkg.go.dev/math/bits)
|
||||
* [math/cmplx](https://pkg.go.dev/math/cmplx)
|
||||
* [math/rand](https://pkg.go.dev/math/rand)
|
||||
* [net/url](https://pkg.go.dev/net/url)
|
||||
* [errors](https://pkg.go.dev/errors)
|
||||
* [context](https://pkg.go.dev/context)
|
||||
* [io](https://pkg.go.dev/io)
|
||||
* [io/fs](https://pkg.go.dev/io/fs)
|
||||
* [io/ioutil](https://pkg.go.dev/io/ioutil)
|
||||
* [log](https://pkg.go.dev/log)
|
||||
* [flag](https://pkg.go.dev/flag)
|
||||
* [sort](https://pkg.go.dev/sort)
|
||||
@@ -294,6 +297,7 @@ Here are the Go packages that can be imported correctly:
|
||||
* [fmt](https://pkg.go.dev/fmt) (partially)
|
||||
* [reflect](https://pkg.go.dev/reflect) (partially)
|
||||
* [time](https://pkg.go.dev/time) (partially)
|
||||
* [encoding](https://pkg.go.dev/encoding)
|
||||
* [encoding/binary](https://pkg.go.dev/encoding/binary)
|
||||
* [encoding/hex](https://pkg.go.dev/encoding/hex)
|
||||
* [encoding/base32](https://pkg.go.dev/encoding/base32)
|
||||
@@ -303,9 +307,16 @@ Here are the Go packages that can be imported correctly:
|
||||
* [hash/adler32](https://pkg.go.dev/hash/adler32)
|
||||
* [hash/crc32](https://pkg.go.dev/hash/crc32) (partially)
|
||||
* [hash/crc64](https://pkg.go.dev/hash/crc64)
|
||||
* [crypto](https://pkg.go.dev/crypto)
|
||||
* [crypto/md5](https://pkg.go.dev/crypto/md5)
|
||||
* [crypto/sha1](https://pkg.go.dev/crypto/sha1)
|
||||
* [crypto/sha256](https://pkg.go.dev/crypto/sha256)
|
||||
* [crypto/sha512](https://pkg.go.dev/crypto/sha512) (partially)
|
||||
* [crypto/rand](https://pkg.go.dev/crypto/rand) (partially)
|
||||
* [regexp](https://pkg.go.dev/regexp)
|
||||
* [regexp/syntax](https://pkg.go.dev/regexp/syntax)
|
||||
* [go/token](https://pkg.go.dev/go/token)
|
||||
* [go/scanner](https://pkg.go.dev/go/scanner)
|
||||
|
||||
|
||||
## Dependencies
|
||||
@@ -317,8 +328,7 @@ Here are the Go packages that can be imported correctly:
|
||||
- [pkg-config 0.29+](https://www.freedesktop.org/wiki/Software/pkg-config/)
|
||||
- [bdwgc/libgc 8.0+](https://www.hboehm.info/gc/)
|
||||
- [OpenSSL 3.0+](https://www.openssl.org/)
|
||||
- [cJSON 1.7+](https://github.com/DaveGamble/cJSON) (optional, for [github.com/goplus/llgo/c/cjson](https://pkg.go.dev/github.com/goplus/llgo/c/cjson))
|
||||
- [SQLite 3](https://www.sqlite.org) (optional, for [github.com/goplus/llgo/c/sqlite](https://pkg.go.dev/github.com/goplus/llgo/c/sqlite))
|
||||
- [zlib 1.2+](https://www.zlib.net)
|
||||
- [Python 3.12+](https://www.python.org) (optional, for [github.com/goplus/llgo/py](https://pkg.go.dev/github.com/goplus/llgo/py))
|
||||
|
||||
## How to install
|
||||
@@ -328,10 +338,9 @@ Follow these steps to generate the `llgo` command (its usage is the same as the
|
||||
### on macOS
|
||||
|
||||
```sh
|
||||
brew update # execute if needed
|
||||
brew update
|
||||
brew install llvm@18 pkg-config bdw-gc openssl
|
||||
brew install cjson sqlite python@3.12 # optional
|
||||
export PATH=$(brew --prefix llvm@18)/bin:$PATH # you may want to add this to your shell RC file, e.g. ~/.zshrc
|
||||
brew install python@3.12 # optional
|
||||
go install -v github.com/goplus/llgo/cmd/llgo@latest
|
||||
```
|
||||
|
||||
@@ -340,10 +349,9 @@ go install -v github.com/goplus/llgo/cmd/llgo@latest
|
||||
```sh
|
||||
echo "deb http://apt.llvm.org/$(lsb_release -cs)/ llvm-toolchain-$(lsb_release -cs)-18 main" | sudo tee /etc/apt/sources.list.d/llvm.list
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||
sudo apt-get update # execute if needed
|
||||
sudo apt-get install -y llvm-18-dev clang-18 lld-18 pkg-config libgc-dev libssl-dev
|
||||
sudo apt-get install -y libcjson-dev libsqlite3-dev python3.12-dev # optional
|
||||
export PATH=/usr/lib/llvm-18/bin:$PATH # you may want to add this to your shell RC file, e.g. ~/.bashrc
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y llvm-18-dev clang-18 lld-18 pkg-config libgc-dev libssl-dev zlib1g-dev
|
||||
sudo apt-get install -y python3.12-dev # optional
|
||||
go install -v github.com/goplus/llgo/cmd/llgo@latest
|
||||
```
|
||||
|
||||
|
||||
34
_cmptest/_goparsedemo/parse.go
Normal file
34
_cmptest/_goparsedemo/parse.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fset := token.NewFileSet() // positions are relative to fset
|
||||
|
||||
src := `package foo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func bar() {
|
||||
fmt.Println(time.Now())
|
||||
}`
|
||||
|
||||
// Parse src but stop after processing the imports.
|
||||
f, err := parser.ParseFile(fset, "", src, parser.ImportsOnly)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Print the imports from the file's AST.
|
||||
for _, s := range f.Imports {
|
||||
fmt.Println(s.Path.Value)
|
||||
}
|
||||
}
|
||||
25
_cmptest/bigintdemo/fib.go
Normal file
25
_cmptest/bigintdemo/fib.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Initialize two big ints with the first two numbers in the sequence.
|
||||
a := big.NewInt(0)
|
||||
b := big.NewInt(1)
|
||||
|
||||
// Initialize limit as 10^99, the smallest integer with 100 digits.
|
||||
var limit big.Int
|
||||
limit.Exp(big.NewInt(10), big.NewInt(99), nil)
|
||||
|
||||
// Loop while a is smaller than 1e100.
|
||||
for a.Cmp(&limit) < 0 {
|
||||
// Compute the next Fibonacci number, storing it in a.
|
||||
a.Add(a, b)
|
||||
// Swap a and b so that b is the next number in the sequence.
|
||||
a, b = b, a
|
||||
}
|
||||
fmt.Println(a) // 100-digit Fibonacci number
|
||||
}
|
||||
27
_cmptest/goscandemo/scan.go
Normal file
27
_cmptest/goscandemo/scan.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// src is the input that we want to tokenize.
|
||||
src := []byte("cos(x) + 1i*sin(x) // Euler")
|
||||
|
||||
// Initialize the scanner.
|
||||
var s scanner.Scanner
|
||||
fset := token.NewFileSet() // positions are relative to fset
|
||||
file := fset.AddFile("", fset.Base(), len(src)) // register input "file"
|
||||
s.Init(file, src, nil /* no error handler */, scanner.ScanComments)
|
||||
|
||||
// Repeated calls to Scan yield the token sequence found in the input.
|
||||
for {
|
||||
pos, tok, lit := s.Scan()
|
||||
if tok == token.EOF {
|
||||
break
|
||||
}
|
||||
fmt.Printf("%s\t%s\t%q\n", fset.Position(pos), tok, lit)
|
||||
}
|
||||
}
|
||||
19
_cmptest/ioutildemo/ioutil.go
Normal file
19
_cmptest/ioutildemo/ioutil.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
r := strings.NewReader("Go is a general-purpose language designed with systems programming in mind.")
|
||||
|
||||
b, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", b)
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -10,5 +11,10 @@ func main() {
|
||||
h := md5.New()
|
||||
io.WriteString(h, "The fog is getting thicker!")
|
||||
io.WriteString(h, "And Leon's getting laaarger!")
|
||||
fmt.Printf("%x", h.Sum(nil))
|
||||
fmt.Printf("%x\n", h.Sum(nil))
|
||||
|
||||
h = crypto.MD5.New()
|
||||
io.WriteString(h, "The fog is getting thicker!")
|
||||
io.WriteString(h, "And Leon's getting laaarger!")
|
||||
fmt.Printf("%x\n", h.Sum(nil))
|
||||
}
|
||||
|
||||
14
_cmptest/sha1demo/sha1.go
Normal file
14
_cmptest/sha1demo/sha1.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
func main() {
|
||||
h := sha1.New()
|
||||
io.WriteString(h, "The fog is getting thicker!")
|
||||
io.WriteString(h, "And Leon's getting laaarger!")
|
||||
fmt.Printf("%x", h.Sum(nil))
|
||||
}
|
||||
14
_cmptest/sha256demo/sha256.go
Normal file
14
_cmptest/sha256demo/sha256.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
func main() {
|
||||
h := sha256.New()
|
||||
io.WriteString(h, "The fog is getting thicker!")
|
||||
io.WriteString(h, "And Leon's getting laaarger!")
|
||||
fmt.Printf("%x", h.Sum(nil))
|
||||
}
|
||||
14
_cmptest/sha512demo/sha512.go
Normal file
14
_cmptest/sha512demo/sha512.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha512"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
func main() {
|
||||
h := sha512.New()
|
||||
io.WriteString(h, "The fog is getting thicker!")
|
||||
io.WriteString(h, "And Leon's getting laaarger!")
|
||||
fmt.Printf("%x", h.Sum(nil))
|
||||
}
|
||||
20
_cmptest/urldemo/url.go
Normal file
20
_cmptest/urldemo/url.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
func main() {
|
||||
u, err := url.Parse("http://foo.example.com/foo?bar=1")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
u.Scheme = "https"
|
||||
u.Host = "bar.example.com"
|
||||
q := u.Query()
|
||||
q.Set("bar", "2")
|
||||
u.RawQuery = q.Encode()
|
||||
fmt.Println(u)
|
||||
}
|
||||
18
_demo/randcrypt/rand.go
Normal file
18
_demo/randcrypt/rand.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
c := 10
|
||||
b := make([]byte, c)
|
||||
_, err := rand.Read(b)
|
||||
if err != nil {
|
||||
fmt.Println("error:", err)
|
||||
return
|
||||
}
|
||||
// The slice should now contain random bytes instead of only zeroes.
|
||||
fmt.Printf("%x\n", b)
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
type Context struct {
|
||||
namespaceName string
|
||||
className string
|
||||
unit *clang.TranslationUnit
|
||||
}
|
||||
|
||||
func newContext() *Context {
|
||||
@@ -26,23 +27,51 @@ func (c *Context) setClassName(name string) {
|
||||
c.className = name
|
||||
}
|
||||
|
||||
func (c *Context) setUnit(unit *clang.TranslationUnit) {
|
||||
c.unit = unit
|
||||
}
|
||||
|
||||
var context = newContext()
|
||||
|
||||
func print_cursor_info(cursor clang.Cursor) {
|
||||
func printCursorLocation(cursor clang.Cursor) {
|
||||
loc := cursor.Location()
|
||||
var file clang.File
|
||||
var line, column c.Uint
|
||||
|
||||
loc.SpellingLocation(&file, &line, &column, nil)
|
||||
filename := file.FileName()
|
||||
defer filename.Dispose()
|
||||
|
||||
c.Printf(c.Str("%s:%d:%d\n"), filename.CStr(), line, column)
|
||||
}
|
||||
|
||||
func printMarcoInfo(cursor clang.Cursor) {
|
||||
printCursorLocation(cursor)
|
||||
name := cursor.String()
|
||||
c.Printf(c.Str("Marco Name: %s\n"), name.CStr())
|
||||
ran := cursor.Extent()
|
||||
var numTokens c.Uint
|
||||
var tokens *clang.Token
|
||||
context.unit.Tokenize(ran, &tokens, &numTokens)
|
||||
c.Printf(c.Str("Content: "))
|
||||
|
||||
tokensSlice := unsafe.Slice(tokens, int(numTokens))
|
||||
for _, tok := range tokensSlice {
|
||||
tokStr := context.unit.Token(tok)
|
||||
c.Printf(c.Str("%s "), tokStr.CStr())
|
||||
tokStr.Dispose()
|
||||
}
|
||||
|
||||
c.Printf(c.Str("\n"))
|
||||
println("--------------------------------")
|
||||
}
|
||||
func printFuncInfo(cursor clang.Cursor) {
|
||||
printCursorLocation(cursor)
|
||||
|
||||
cursorStr := cursor.String()
|
||||
symbol := cursor.Mangling()
|
||||
defer symbol.Dispose()
|
||||
defer cursorStr.Dispose()
|
||||
defer filename.Dispose()
|
||||
|
||||
if context.namespaceName != "" && context.className != "" {
|
||||
fmt.Printf("%s:%s:", context.namespaceName, context.className)
|
||||
@@ -78,7 +107,9 @@ func print_cursor_info(cursor clang.Cursor) {
|
||||
}
|
||||
|
||||
func visit(cursor, parent clang.Cursor, clientData c.Pointer) clang.ChildVisitResult {
|
||||
if cursor.Kind == clang.Namespace {
|
||||
if cursor.Kind == clang.MacroDefinition {
|
||||
printMarcoInfo(cursor)
|
||||
} else if cursor.Kind == clang.Namespace {
|
||||
nameStr := cursor.String()
|
||||
context.setNamespaceName(c.GoString(nameStr.CStr()))
|
||||
clang.VisitChildren(cursor, visit, nil)
|
||||
@@ -89,7 +120,7 @@ func visit(cursor, parent clang.Cursor, clientData c.Pointer) clang.ChildVisitRe
|
||||
clang.VisitChildren(cursor, visit, nil)
|
||||
context.setClassName("")
|
||||
} else if cursor.Kind == clang.CXXMethod || cursor.Kind == clang.FunctionDecl {
|
||||
print_cursor_info(cursor)
|
||||
printFuncInfo(cursor)
|
||||
}
|
||||
|
||||
return clang.ChildVisit_Continue
|
||||
@@ -105,7 +136,7 @@ func parse(filename *c.Char) {
|
||||
filename,
|
||||
unsafe.SliceData(args), 3,
|
||||
nil, 0,
|
||||
clang.TranslationUnit_None,
|
||||
clang.DetailedPreprocessingRecord,
|
||||
)
|
||||
|
||||
if unit == nil {
|
||||
@@ -113,6 +144,7 @@ func parse(filename *c.Char) {
|
||||
c.Exit(1)
|
||||
}
|
||||
|
||||
context.setUnit(unit)
|
||||
cursor := unit.Cursor()
|
||||
|
||||
clang.VisitChildren(cursor, visit, nil)
|
||||
|
||||
@@ -35,6 +35,10 @@ void wrap_clang_getCursorResultType(CXCursor *cur, CXType *typ) { *typ = clang_g
|
||||
|
||||
CXString wrap_clang_getTypeSpelling(CXType *typ) { return clang_getTypeSpelling(*typ); }
|
||||
|
||||
CXString wrap_clang_getTokenSpelling(CXTranslationUnit unit, CXToken *token) {
|
||||
return clang_getTokenSpelling(unit, *token);
|
||||
}
|
||||
|
||||
void wrap_clang_getCursorLocation(CXCursor *cur, CXSourceLocation *loc) { *loc = clang_getCursorLocation(*cur); }
|
||||
|
||||
void wrap_clang_getSpellingLocation(CXSourceLocation *loc, CXFile *file, unsigned *line, unsigned *column,
|
||||
@@ -42,6 +46,12 @@ void wrap_clang_getSpellingLocation(CXSourceLocation *loc, CXFile *file, unsigne
|
||||
clang_getSpellingLocation(*loc, file, line, column, offset);
|
||||
}
|
||||
|
||||
void wrap_clang_getCursorExtent(CXCursor *cur, CXSourceRange *range) { *range = clang_getCursorExtent(*cur); }
|
||||
|
||||
void wrap_clang_tokenize(CXTranslationUnit unit, CXSourceRange *Range, CXToken **Tokens, unsigned *NumTokens) {
|
||||
clang_tokenize(unit, *Range, Tokens, NumTokens);
|
||||
}
|
||||
|
||||
unsigned wrap_clang_visitChildren(CXCursor *parent, wrap_CXCursorVisitor visitor, CXClientData client_data) {
|
||||
wrap_data data = {client_data, visitor};
|
||||
return clang_visitChildren(*parent, wrap_visitor, CXClientData(&data));
|
||||
|
||||
151
c/clang/clang.go
151
c/clang/clang.go
@@ -1225,7 +1225,22 @@ func (*Index) Dispose() {}
|
||||
* constructing the translation unit.
|
||||
*/
|
||||
const (
|
||||
/**
|
||||
* Used to indicate that no special translation-unit options are
|
||||
* needed.
|
||||
*/
|
||||
TranslationUnit_None = 0x0
|
||||
/**
|
||||
* Used to indicate that the parser should construct a "detailed"
|
||||
* preprocessing record, including all macro definitions and instantiations.
|
||||
*
|
||||
* Constructing a detailed preprocessing record requires more memory
|
||||
* and time to parse, since the information contained in the record
|
||||
* is usually not retained. However, it can be useful for
|
||||
* applications that require more detailed information about the
|
||||
* behavior of the preprocessor.
|
||||
*/
|
||||
DetailedPreprocessingRecord = 0x01
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -1330,6 +1345,52 @@ type SourceLocation struct {
|
||||
intData c.Uint
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies a half-open character range in the source code.
|
||||
*
|
||||
* Use clang_getRangeStart() and clang_getRangeEnd() to retrieve the
|
||||
* starting and end locations from a source range, respectively.
|
||||
*/
|
||||
type SourceRange struct {
|
||||
ptrData [2]c.Pointer
|
||||
beginIntData c.Uint
|
||||
endIntData c.Uint
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes a kind of token.
|
||||
*/
|
||||
type TokenKind c.Int
|
||||
|
||||
const (
|
||||
/**
|
||||
* A token that contains some kind of punctuation.
|
||||
*/
|
||||
Punctuation TokenKind = iota
|
||||
/**
|
||||
* A language keyword.
|
||||
*/
|
||||
Keyword
|
||||
|
||||
/**
|
||||
* An identifier (that is not a keyword).
|
||||
*/
|
||||
Identifier
|
||||
/**
|
||||
* A numeric, string, or character literal.
|
||||
*/
|
||||
Literal
|
||||
/**
|
||||
* A comment.
|
||||
*/
|
||||
Comment
|
||||
)
|
||||
|
||||
type Token struct {
|
||||
intData [4]c.Uint
|
||||
ptrData c.Pointer
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a name for the entity referenced by this cursor.
|
||||
*/
|
||||
@@ -1409,6 +1470,16 @@ func (c Cursor) Argument(index c.Uint) (arg Cursor) {
|
||||
return
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the physical location of the source constructor referenced
|
||||
* by the given cursor.
|
||||
*
|
||||
* The location of a declaration is typically the location of the name of that
|
||||
* declaration, where the name of that declaration would occur if it is
|
||||
* unnamed, or some keyword that introduces that particular declaration.
|
||||
* The location of a reference is where that reference occurs within the
|
||||
* source code.
|
||||
*/
|
||||
// llgo:link (*Cursor).wrapLocation C.wrap_clang_getCursorLocation
|
||||
func (c *Cursor) wrapLocation(loc *SourceLocation) {}
|
||||
|
||||
@@ -1417,6 +1488,86 @@ func (c Cursor) Location() (loc SourceLocation) {
|
||||
return
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the physical extent of the source construct referenced by
|
||||
* the given cursor.
|
||||
*
|
||||
* The extent of a cursor starts with the file/line/column pointing at the
|
||||
* first character within the source construct that the cursor refers to and
|
||||
* ends with the last character within that source construct. For a
|
||||
* declaration, the extent covers the declaration itself. For a reference,
|
||||
* the extent covers the location of the reference (e.g., where the referenced
|
||||
* entity was actually used).
|
||||
*/
|
||||
// llgo:link (*Cursor).wrapExtent C.wrap_clang_getCursorExtent
|
||||
func (c *Cursor) wrapExtent(loc *SourceRange) {}
|
||||
|
||||
func (c Cursor) Extent() (loc SourceRange) {
|
||||
c.wrapExtent(&loc)
|
||||
return
|
||||
}
|
||||
|
||||
/**
|
||||
* Tokenize the source code described by the given range into raw
|
||||
* lexical tokens.
|
||||
*
|
||||
* \param TU the translation unit whose text is being tokenized.
|
||||
*
|
||||
* \param Range the source range in which text should be tokenized. All of the
|
||||
* tokens produced by tokenization will fall within this source range,
|
||||
*
|
||||
* \param Tokens this pointer will be set to point to the array of tokens
|
||||
* that occur within the given source range. The returned pointer must be
|
||||
* freed with clang_disposeTokens() before the translation unit is destroyed.
|
||||
*
|
||||
* \param NumTokens will be set to the number of tokens in the \c *Tokens
|
||||
* array.
|
||||
*
|
||||
*/
|
||||
// llgo:link (*TranslationUnit).wrapTokenize C.wrap_clang_tokenize
|
||||
func (t *TranslationUnit) wrapTokenize(ran *SourceRange, tokens **Token, numTokens *c.Uint) {}
|
||||
|
||||
func (t *TranslationUnit) Tokenize(ran SourceRange, tokens **Token, numTokens *c.Uint) {
|
||||
t.wrapTokenize(&ran, tokens, numTokens)
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the spelling of the given token.
|
||||
*
|
||||
* The spelling of a token is the textual representation of that token, e.g.,
|
||||
* the text of an identifier or keyword.
|
||||
*/
|
||||
// llgo:link (*TranslationUnit).wrapToken C.wrap_clang_getTokenSpelling
|
||||
func (*TranslationUnit) wrapToken(token *Token) (ret String) {
|
||||
return
|
||||
}
|
||||
|
||||
func (c *TranslationUnit) Token(token Token) (ret String) {
|
||||
return c.wrapToken(&token)
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the file, line, column, and offset represented by
|
||||
* the given source location.
|
||||
*
|
||||
* If the location refers into a macro instantiation, return where the
|
||||
* location was originally spelled in the source file.
|
||||
*
|
||||
* \param location the location within a source file that will be decomposed
|
||||
* into its parts.
|
||||
*
|
||||
* \param file [out] if non-NULL, will be set to the file to which the given
|
||||
* source location points.
|
||||
*
|
||||
* \param line [out] if non-NULL, will be set to the line to which the given
|
||||
* source location points.
|
||||
*
|
||||
* \param column [out] if non-NULL, will be set to the column to which the given
|
||||
* source location points.
|
||||
*
|
||||
* \param offset [out] if non-NULL, will be set to the offset into the
|
||||
* buffer to which the given source location points.
|
||||
*/
|
||||
// llgo:link (*SourceLocation).wrapSpellingLocation C.wrap_clang_getSpellingLocation
|
||||
func (l *SourceLocation) wrapSpellingLocation(file *File, line, column, offset *c.Uint) {}
|
||||
|
||||
|
||||
@@ -13,11 +13,11 @@ const BUFFER_SIZE = 1024
|
||||
var (
|
||||
loop *libuv.Loop
|
||||
openReq libuv.Fs
|
||||
readReq libuv.Fs
|
||||
closeReq libuv.Fs
|
||||
|
||||
buffer [BUFFER_SIZE]c.Char
|
||||
iov libuv.Buf
|
||||
file libuv.File
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -31,7 +31,11 @@ func main() {
|
||||
libuv.FsOpen(loop, &openReq, c.Str("example.txt"), os.O_RDONLY, 0, onOpen)
|
||||
|
||||
// Run the loop
|
||||
libuv.Run(loop, libuv.RUN_DEFAULT)
|
||||
result := libuv.Run(loop, 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()
|
||||
@@ -40,32 +44,45 @@ func main() {
|
||||
func onOpen(req *libuv.Fs) {
|
||||
// Check for errors
|
||||
if libuv.FsGetResult(req) < 0 {
|
||||
c.Fprintf(c.Stderr, c.Str("Error opening file: %s\n"), libuv.Strerror(libuv.Errno(libuv.LoopClose(loop))))
|
||||
c.Fprintf(c.Stderr, c.Str("Error opening file: %s\n"), libuv.Strerror(libuv.Errno(libuv.FsGetResult(req))))
|
||||
libuv.LoopClose(loop)
|
||||
return
|
||||
}
|
||||
|
||||
// Store the file descriptor
|
||||
file = libuv.File(libuv.FsGetResult(req))
|
||||
|
||||
// Init buffer
|
||||
iov = libuv.InitBuf((*c.Char)(unsafe.Pointer(&buffer[0])), c.Uint(unsafe.Sizeof(buffer)))
|
||||
|
||||
// Read the file
|
||||
readRes := libuv.FsRead(loop, &readReq, libuv.File(libuv.FsGetResult(req)), &iov, 1, -1, onRead)
|
||||
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)
|
||||
libuv.FsReqCleanup(&readReq)
|
||||
libuv.LoopClose(loop)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func onRead(req *libuv.Fs) {
|
||||
// Cleanup the request
|
||||
defer libuv.FsReqCleanup(req)
|
||||
// Check for errors
|
||||
if libuv.FsGetResult(req) < 0 {
|
||||
c.Fprintf(c.Stderr, c.Str("Read error: %s\n"), libuv.Strerror(libuv.Errno(libuv.FsGetResult(req))))
|
||||
libuv.LoopClose(loop)
|
||||
} else if libuv.FsGetResult(req) == 0 {
|
||||
c.Printf(c.Str("EOF\n"))
|
||||
// Close the file
|
||||
closeRes := libuv.FsClose(loop, &closeReq, libuv.File(libuv.FsGetResult(&openReq)), onClose)
|
||||
if closeRes != 0 {
|
||||
// Print the content
|
||||
c.Printf(c.Str("Error in FsClose: %s (code: %d)\n"), libuv.Strerror(libuv.Errno(closeRes)), closeRes)
|
||||
libuv.LoopClose(loop)
|
||||
return
|
||||
@@ -73,7 +90,8 @@ func onRead(req *libuv.Fs) {
|
||||
} else {
|
||||
c.Printf(c.Str("Read %d bytes\n"), libuv.FsGetResult(req))
|
||||
c.Printf(c.Str("Read content: %.*s\n"), libuv.FsGetResult(req), (*c.Char)(unsafe.Pointer(&buffer[0])))
|
||||
libuv.LoopClose(loop)
|
||||
// Read the file again
|
||||
readFile()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,14 +102,15 @@ func onClose(req *libuv.Fs) {
|
||||
} else {
|
||||
c.Printf(c.Str("\nFile closed successfully.\n"))
|
||||
}
|
||||
libuv.LoopClose(loop)
|
||||
}
|
||||
|
||||
func cleanup() {
|
||||
// Cleanup the requests
|
||||
libuv.FsReqCleanup(&openReq)
|
||||
libuv.FsReqCleanup(&readReq)
|
||||
libuv.FsReqCleanup(&closeReq)
|
||||
// Close the loop
|
||||
libuv.LoopClose(loop)
|
||||
result := libuv.LoopClose(loop)
|
||||
if result != 0 {
|
||||
c.Fprintf(c.Stderr, c.Str("Error in LoopClose: %s\n"), libuv.Strerror(libuv.Errno(result)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,6 @@ import (
|
||||
var DEFAULT_PORT c.Int = 8080
|
||||
var DEFAULT_BACKLOG c.Int = 128
|
||||
|
||||
var loop *libuv.Loop
|
||||
|
||||
type WriteReq struct {
|
||||
Req libuv.Write
|
||||
Buf libuv.Buf
|
||||
@@ -20,7 +18,7 @@ type WriteReq struct {
|
||||
|
||||
func main() {
|
||||
// Initialize the default event loop
|
||||
loop = libuv.DefaultLoop()
|
||||
var loop = libuv.DefaultLoop()
|
||||
|
||||
// Initialize a TCP server
|
||||
var server libuv.Tcp
|
||||
@@ -105,7 +103,7 @@ func OnNewConnection(server *libuv.Stream, status c.Int) {
|
||||
}
|
||||
|
||||
// Initialize the client TCP handle.
|
||||
if libuv.InitTcp(loop, client) < 0 {
|
||||
if libuv.InitTcp(libuv.DefaultLoop(), client) < 0 {
|
||||
c.Fprintf(c.Stderr, c.Str("Failed to initialize client\n"))
|
||||
c.Free(c.Pointer(client))
|
||||
return
|
||||
|
||||
@@ -69,7 +69,7 @@ type File c.Int
|
||||
/* Handle types. */
|
||||
|
||||
type Fs struct {
|
||||
Unused [0]byte
|
||||
Unused [440]byte
|
||||
}
|
||||
|
||||
type FsEvent struct {
|
||||
|
||||
218
c/libuv/libuv.go
218
c/libuv/libuv.go
@@ -97,44 +97,16 @@ type Loop struct {
|
||||
Unused [0]byte
|
||||
}
|
||||
|
||||
type Handle struct {
|
||||
Unused [96]byte
|
||||
}
|
||||
|
||||
type Stream struct {
|
||||
Unused [264]byte
|
||||
}
|
||||
|
||||
type Poll struct {
|
||||
Unused [0]byte
|
||||
}
|
||||
|
||||
/* Request types. */
|
||||
|
||||
type Req struct {
|
||||
Unused [0]byte
|
||||
}
|
||||
|
||||
type GetAddrInfo struct {
|
||||
Unused [0]byte
|
||||
}
|
||||
|
||||
type GetNameInfo struct {
|
||||
Unused [0]byte
|
||||
}
|
||||
|
||||
type Shutdown struct {
|
||||
Unused [0]byte
|
||||
}
|
||||
|
||||
type Write struct {
|
||||
Unused [192]byte
|
||||
}
|
||||
|
||||
type Connect struct {
|
||||
Unused [0]byte
|
||||
}
|
||||
|
||||
type Buf struct {
|
||||
Base *c.Char
|
||||
Len uintptr
|
||||
@@ -157,21 +129,12 @@ type FreeFunc func(ptr c.Pointer)
|
||||
// llgo:type C
|
||||
type AllocCb func(handle *Handle, suggestedSize uintptr, buf *Buf)
|
||||
|
||||
// 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 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 ConnectionCb func(server *Stream, status c.Int)
|
||||
|
||||
// llgo:type C
|
||||
type ShutdownCb func(req *Shutdown, status c.Int)
|
||||
|
||||
@@ -204,170 +167,6 @@ func (shutdown *Shutdown) Shutdown(stream *Stream, shutdownCb ShutdownCb) c.Int
|
||||
|
||||
// ----------------------------------------------
|
||||
|
||||
/* Handle related function and method */
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
//go:linkname HandleSize C.uv_handle_size
|
||||
func HandleSize(handleType HandleType) uintptr
|
||||
|
||||
// llgo:link (*Handle).GetType C.uv_handle_get_type
|
||||
func (handle *Handle) GetType() HandleType {
|
||||
return 0
|
||||
}
|
||||
|
||||
//go:linkname HandleTypeName C.uv_handle_type_name
|
||||
func HandleTypeName(handleType HandleType) *c.Char
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
//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
|
||||
}
|
||||
|
||||
// llgo:link (*Handle).IsClosing C.uv_is_closing
|
||||
func (handle *Handle) IsClosing() c.Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// ----------------------------------------------
|
||||
|
||||
/* Req related function and method */
|
||||
|
||||
//go:linkname ReqSize C.uv_req_size
|
||||
func ReqSize(reqType ReqType) uintptr
|
||||
|
||||
// llgo:link (*Req).GetData C.uv_req_get_data
|
||||
func (req *Req) GetData() c.Pointer {
|
||||
return nil
|
||||
}
|
||||
|
||||
// llgo:link (*Req).SetData C.uv_handle_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
|
||||
}
|
||||
|
||||
//go:linkname TypeName C.uv_req_type_name
|
||||
func TypeName(reqType ReqType) *c.Char
|
||||
|
||||
// ----------------------------------------------
|
||||
|
||||
/* 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
|
||||
}
|
||||
|
||||
// ----------------------------------------------
|
||||
|
||||
/* Loop related functions and method. */
|
||||
|
||||
//go:linkname LoopSize C.uv_loop_size
|
||||
@@ -437,20 +236,3 @@ func PollStop(handle *Poll) c.Int
|
||||
|
||||
//go:linkname PollInitSocket C.uv_poll_init_socket
|
||||
func PollInitSocket(loop *Loop, handle *Poll, socket c.Int) 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
|
||||
|
||||
230
c/libuv/net.go
230
c/libuv/net.go
@@ -65,8 +65,22 @@ 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 {
|
||||
Unused [264]byte
|
||||
Data c.Pointer
|
||||
Unused [256]byte
|
||||
}
|
||||
|
||||
type Udp struct {
|
||||
@@ -75,10 +89,34 @@ type Udp struct {
|
||||
|
||||
/* Request types. */
|
||||
|
||||
type Req struct {
|
||||
Unused [0]byte
|
||||
}
|
||||
|
||||
type UdpSend struct {
|
||||
Unused [0]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
|
||||
}
|
||||
|
||||
type GetAddrInfo struct {
|
||||
Unused [0]byte
|
||||
}
|
||||
|
||||
type GetNameInfo struct {
|
||||
Unused [0]byte
|
||||
}
|
||||
|
||||
// ----------------------------------------------
|
||||
|
||||
/* Function type */
|
||||
@@ -95,6 +133,179 @@ 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)
|
||||
|
||||
// ----------------------------------------------
|
||||
|
||||
/* Handle related function and method */
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
//go:linkname HandleSize C.uv_handle_size
|
||||
func HandleSize(handleType HandleType) uintptr
|
||||
|
||||
// llgo:link (*Handle).GetType C.uv_handle_get_type
|
||||
func (handle *Handle) GetType() HandleType {
|
||||
return 0
|
||||
}
|
||||
|
||||
//go:linkname HandleTypeName C.uv_handle_type_name
|
||||
func HandleTypeName(handleType HandleType) *c.Char
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
//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
|
||||
}
|
||||
|
||||
// llgo:link (*Handle).IsClosing C.uv_is_closing
|
||||
func (handle *Handle) IsClosing() c.Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// ----------------------------------------------
|
||||
|
||||
/* Req related function and method */
|
||||
|
||||
//go:linkname ReqSize C.uv_req_size
|
||||
func ReqSize(reqType ReqType) uintptr
|
||||
|
||||
// llgo:link (*Req).GetData C.uv_req_get_data
|
||||
func (req *Req) GetData() c.Pointer {
|
||||
return nil
|
||||
}
|
||||
|
||||
// llgo:link (*Req).SetData C.uv_handle_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
|
||||
}
|
||||
|
||||
//go:linkname TypeName C.uv_req_type_name
|
||||
func TypeName(reqType ReqType) *c.Char
|
||||
|
||||
// ----------------------------------------------
|
||||
|
||||
/* 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
|
||||
}
|
||||
|
||||
// ----------------------------------------------
|
||||
|
||||
/* Tcp related function and method */
|
||||
@@ -273,3 +484,20 @@ 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
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
/* Handle types. */
|
||||
|
||||
// TODO(spongehah): Timer
|
||||
type Timer struct {
|
||||
Unused [0]byte
|
||||
}
|
||||
|
||||
44
c/openssl/_demo/cbigintdemo/fib.go
Normal file
44
c/openssl/_demo/cbigintdemo/fib.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/goplus/llgo/c"
|
||||
"github.com/goplus/llgo/c/openssl"
|
||||
)
|
||||
|
||||
func newInt(n openssl.BN_ULONG) *openssl.BIGNUM {
|
||||
ret := openssl.BNNew()
|
||||
ret.SetWord(n)
|
||||
return ret
|
||||
}
|
||||
|
||||
func main() {
|
||||
ctx := openssl.BN_CTXNew()
|
||||
defer ctx.Free()
|
||||
|
||||
// Initialize two big ints with the first two numbers in the sequence.
|
||||
a := newInt(0)
|
||||
b := newInt(1)
|
||||
defer a.Free()
|
||||
defer b.Free()
|
||||
|
||||
// Initialize limit as 10^99, the smallest integer with 100 digits.
|
||||
v10, v99 := newInt(10), newInt(99)
|
||||
defer v10.Free()
|
||||
defer v99.Free()
|
||||
|
||||
limit := openssl.BNNew()
|
||||
defer limit.Free()
|
||||
|
||||
limit.Exp(v10, v99, ctx)
|
||||
|
||||
// Loop while a is smaller than 1e100.
|
||||
for a.Cmp(limit) < 0 {
|
||||
// Compute the next Fibonacci number, storing it in a.
|
||||
a.Add(a, b)
|
||||
// Swap a and b so that b is the next number in the sequence.
|
||||
a, b = b, a
|
||||
}
|
||||
cstr := a.CStr()
|
||||
c.Printf(c.Str("%s\n"), cstr) // 100-digit Fibonacci number
|
||||
openssl.FreeCStr(cstr)
|
||||
}
|
||||
45
c/openssl/_demo/chmacdemo/hmac.go
Normal file
45
c/openssl/_demo/chmacdemo/hmac.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goplus/llgo/c"
|
||||
"github.com/goplus/llgo/c/openssl"
|
||||
)
|
||||
|
||||
func main() {
|
||||
str := "Hello, World!"
|
||||
key := "123456"
|
||||
var lenKey = len(key)
|
||||
|
||||
var digest = make([]byte, openssl.EVP_MAX_MD_SIZE)
|
||||
var digestLen c.Uint
|
||||
ctx := openssl.NewHMAC_CTX()
|
||||
if ctx == nil {
|
||||
c.Fprintf(c.Stderr, c.Str("%s\n"), c.Str("Error creating HMAC_CTX"))
|
||||
return
|
||||
}
|
||||
defer ctx.Free()
|
||||
|
||||
var ret c.Int = ctx.InitEx(unsafe.Pointer(unsafe.StringData(key)), c.Int(lenKey), openssl.EVP_sha256(), nil)
|
||||
if ret == 0 {
|
||||
c.Fprintf(c.Stderr, c.Str("%s\n"), c.Str("Error initializing HMAC_CTX"))
|
||||
return
|
||||
}
|
||||
ret = ctx.UpdateString(str)
|
||||
if ret == 0 {
|
||||
c.Fprintf(c.Stderr, c.Str("%s\n"), c.Str("Error updating HMAC_CTX"))
|
||||
return
|
||||
}
|
||||
ret = ctx.Final(unsafe.SliceData(digest), &digestLen)
|
||||
if ret == 0 {
|
||||
c.Fprintf(c.Stderr, c.Str("%s\n"), c.Str("Error finalizing HMAC_CTX"))
|
||||
return
|
||||
}
|
||||
fmt.Print("HMAC: ")
|
||||
for i := 0; i < int(digestLen); i++ {
|
||||
fmt.Printf("%02x", digest[i])
|
||||
}
|
||||
fmt.Print("\n")
|
||||
}
|
||||
17
c/openssl/_demo/cranddemo/rand.go
Normal file
17
c/openssl/_demo/cranddemo/rand.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/goplus/llgo/c/openssl"
|
||||
)
|
||||
|
||||
func main() {
|
||||
b := make([]byte, 10)
|
||||
|
||||
openssl.RANDBytes(b)
|
||||
fmt.Printf("%x\n", b)
|
||||
|
||||
openssl.RANDPrivBytes(b)
|
||||
fmt.Printf("%x\n", b)
|
||||
}
|
||||
5
c/openssl/_wrap/openssl.c
Normal file
5
c/openssl/_wrap/openssl.c
Normal file
@@ -0,0 +1,5 @@
|
||||
#include <openssl/crypto.h>
|
||||
|
||||
void opensslFree(void *ptr) {
|
||||
OPENSSL_free(ptr);
|
||||
}
|
||||
61
c/openssl/bio.go
Normal file
61
c/openssl/bio.go
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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 openssl
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/goplus/llgo/c"
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type BIO struct {
|
||||
Unused [0]byte
|
||||
}
|
||||
|
||||
// BIO *BIO_new_mem_buf(const void *buf, int len);
|
||||
//
|
||||
//go:linkname BIONewMemBuf C.BIO_new_mem_buf
|
||||
func BIONewMemBuf(buf unsafe.Pointer, len c.Int) *BIO
|
||||
|
||||
// int BIO_free(BIO *a);
|
||||
//
|
||||
// llgo:link (*BIO).Free C.BIO_free
|
||||
func (*BIO) Free() c.Int { return 0 }
|
||||
|
||||
// int BIO_up_ref(BIO *a);
|
||||
//
|
||||
// llgo:link (*BIO).UpRef C.BIO_up_ref
|
||||
func (*BIO) UpRef() c.Int { return 0 }
|
||||
|
||||
// int BIO_read_ex(BIO *b, void *data, size_t dlen, size_t *readbytes);
|
||||
//
|
||||
// llgo:link (*BIO).ReadEx C.BIO_read_ex
|
||||
func (*BIO) ReadEx(data unsafe.Pointer, dlen uintptr, readbytes *uintptr) c.Int { return 0 }
|
||||
|
||||
// int BIO_write(BIO *b, const void *data, int dlen);
|
||||
//
|
||||
// llgo:link (*BIO).Write C.BIO_write
|
||||
func (*BIO) Write(data unsafe.Pointer, dlen c.Int) c.Int { return 0 }
|
||||
|
||||
// int BIO_write_ex(BIO *b, const void *data, size_t dlen, size_t *written);
|
||||
//
|
||||
// llgo:link (*BIO).WriteEx C.BIO_write_ex
|
||||
func (*BIO) WriteEx(data unsafe.Pointer, dlen uintptr, written *uintptr) c.Int { return 0 }
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
341
c/openssl/bn.go
Normal file
341
c/openssl/bn.go
Normal file
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
* 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 openssl
|
||||
|
||||
import (
|
||||
_ "unsafe"
|
||||
|
||||
"github.com/goplus/llgo/c"
|
||||
)
|
||||
|
||||
type BN_ULONG = uint64
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type BN_CTX struct {
|
||||
Unused [0]byte
|
||||
}
|
||||
|
||||
// BN_CTX *BN_CTX_new(void);
|
||||
//
|
||||
//go:linkname BN_CTXNew C.BN_CTX_new
|
||||
func BN_CTXNew() *BN_CTX
|
||||
|
||||
// BN_CTX *BN_CTX_secure_new(void);
|
||||
//
|
||||
//go:linkname BN_CTXSecureNew C.BN_CTX_secure_new
|
||||
func BN_CTXSecureNew() *BN_CTX
|
||||
|
||||
// BN_CTX *BN_CTX_new_ex(OSSL_LIB_CTX *ctx);
|
||||
// BN_CTX *BN_CTX_secure_new_ex(OSSL_LIB_CTX *ctx);
|
||||
|
||||
// void BN_CTX_free(BN_CTX *c);
|
||||
//
|
||||
// llgo:link (*BN_CTX).Free C.BN_CTX_free
|
||||
func (*BN_CTX) Free() {}
|
||||
|
||||
// void BN_CTX_start(BN_CTX *ctx);
|
||||
// BIGNUM *BN_CTX_get(BN_CTX *ctx);
|
||||
// void BN_CTX_end(BN_CTX *ctx);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type BIGNUM struct {
|
||||
Unused [0]byte
|
||||
}
|
||||
|
||||
// BIGNUM *BN_new(void);
|
||||
//
|
||||
//go:linkname BNNew C.BN_new
|
||||
func BNNew() *BIGNUM
|
||||
|
||||
// BIGNUM *BN_secure_new(void);
|
||||
//
|
||||
//go:linkname BNSecureNew C.BN_secure_new
|
||||
func BNSecureNew() *BIGNUM
|
||||
|
||||
// void BN_free(BIGNUM *a);
|
||||
//
|
||||
// llgo:link (*BIGNUM).Free C.BN_free
|
||||
func (*BIGNUM) Free() {}
|
||||
|
||||
// void BN_clear_free(BIGNUM *a);
|
||||
//
|
||||
// llgo:link (*BIGNUM).ClearFree C.BN_clear_free
|
||||
func (*BIGNUM) ClearFree() {}
|
||||
|
||||
// void BN_clear(BIGNUM *a);
|
||||
//
|
||||
// llgo:link (*BIGNUM).Clear C.BN_clear
|
||||
func (*BIGNUM) Clear() {}
|
||||
|
||||
// BIGNUM *BN_dup(const BIGNUM *a);
|
||||
//
|
||||
// llgo:link (*BIGNUM).Dup C.BN_dup
|
||||
func (*BIGNUM) Dup() *BIGNUM { return nil }
|
||||
|
||||
// BIGNUM *BN_copy(BIGNUM *a, const BIGNUM *b);
|
||||
//
|
||||
// llgo:link (*BIGNUM).Copy C.BN_copy
|
||||
func (*BIGNUM) Copy(b *BIGNUM) *BIGNUM { return nil }
|
||||
|
||||
// void BN_swap(BIGNUM *a, BIGNUM *b);
|
||||
//
|
||||
// llgo:link (*BIGNUM).Swap C.BN_swap
|
||||
func (*BIGNUM) Swap(b *BIGNUM) {}
|
||||
|
||||
// int BN_is_zero(const BIGNUM *a);
|
||||
//
|
||||
// llgo:link (*BIGNUM).IsZero C.BN_is_zero
|
||||
func (*BIGNUM) IsZero() c.Int { return 0 }
|
||||
|
||||
// void BN_zero_ex(BIGNUM *a);
|
||||
//
|
||||
// llgo:link (*BIGNUM).SetZero C.BN_zero_ex
|
||||
func (*BIGNUM) SetZero() {}
|
||||
|
||||
// int BN_is_one(const BIGNUM *a);
|
||||
//
|
||||
// llgo:link (*BIGNUM).IsOne C.BN_is_one
|
||||
func (*BIGNUM) IsOne() c.Int { return 0 }
|
||||
|
||||
// int BN_is_odd(const BIGNUM *a);
|
||||
//
|
||||
// llgo:link (*BIGNUM).IsOdd C.BN_is_odd
|
||||
func (*BIGNUM) IsOdd() c.Int { return 0 }
|
||||
|
||||
// int BN_abs_is_word(const BIGNUM *a, const BN_ULONG w);
|
||||
//
|
||||
// llgo:link (*BIGNUM).AbsIsWord C.BN_abs_is_word
|
||||
func (*BIGNUM) AbsIsWord(w BN_ULONG) c.Int { return 0 }
|
||||
|
||||
// int BN_is_word(const BIGNUM *a, const BN_ULONG w);
|
||||
//
|
||||
// llgo:link (*BIGNUM).IsWord C.BN_is_word
|
||||
func (*BIGNUM) IsWord(w BN_ULONG) c.Int { return 0 }
|
||||
|
||||
// int BN_set_word(BIGNUM *a, BN_ULONG w);
|
||||
//
|
||||
// llgo:link (*BIGNUM).SetWord C.BN_set_word
|
||||
func (*BIGNUM) SetWord(w BN_ULONG) c.Int { return 0 }
|
||||
|
||||
// BN_ULONG BN_get_word(const BIGNUM *a);
|
||||
//
|
||||
// llgo:link (*BIGNUM).GetWord C.BN_get_word
|
||||
func (*BIGNUM) GetWord() BN_ULONG { return 0 }
|
||||
|
||||
// BN_ULONG BN_mod_word(const BIGNUM *a, BN_ULONG w);
|
||||
//
|
||||
// llgo:link (*BIGNUM).ModWord C.BN_mod_word
|
||||
func (*BIGNUM) ModWord(w BN_ULONG) BN_ULONG { return 0 }
|
||||
|
||||
// BN_ULONG BN_div_word(BIGNUM *a, BN_ULONG w);
|
||||
//
|
||||
// llgo:link (*BIGNUM).DivWord C.BN_div_word
|
||||
func (*BIGNUM) DivWord(w BN_ULONG) BN_ULONG { return 0 }
|
||||
|
||||
// int BN_mul_word(BIGNUM *a, BN_ULONG w);
|
||||
//
|
||||
// llgo:link (*BIGNUM).MulWord C.BN_mul_word
|
||||
func (*BIGNUM) MulWord(w BN_ULONG) c.Int { return 0 }
|
||||
|
||||
// int BN_add_word(BIGNUM *a, BN_ULONG w);
|
||||
//
|
||||
// llgo:link (*BIGNUM).AddWord C.BN_add_word
|
||||
func (*BIGNUM) AddWord(w BN_ULONG) c.Int { return 0 }
|
||||
|
||||
// int BN_sub_word(BIGNUM *a, BN_ULONG w);
|
||||
//
|
||||
// llgo:link (*BIGNUM).SubWord C.BN_sub_word
|
||||
func (*BIGNUM) SubWord(w BN_ULONG) c.Int { return 0 }
|
||||
|
||||
// char *BN_bn2hex(const BIGNUM *a);
|
||||
//
|
||||
// llgo:link (*BIGNUM).Bn2hex C.BN_bn2hex
|
||||
func (*BIGNUM) Bn2hex() *c.Char { return nil }
|
||||
|
||||
// char *BN_bn2dec(const BIGNUM *a);
|
||||
//
|
||||
// llgo:link (*BIGNUM).Bn2dec C.BN_bn2dec
|
||||
func (*BIGNUM) Bn2dec() *c.Char { return nil }
|
||||
|
||||
// llgo:link (*BIGNUM).CStr C.BN_bn2dec
|
||||
func (*BIGNUM) CStr() *c.Char { return nil }
|
||||
|
||||
// int BN_hex2bn(BIGNUM **a, const char *str);
|
||||
//
|
||||
//go:linkname BNHex2bn C.BN_hex2bn
|
||||
func BNHex2bn(a **BIGNUM, str *c.Char) c.Int
|
||||
|
||||
// int BN_dec2bn(BIGNUM **a, const char *str);
|
||||
//
|
||||
//go:linkname BNDec2bn C.BN_dec2bn
|
||||
func BNDec2bn(a **BIGNUM, str *c.Char) c.Int
|
||||
|
||||
// int BN_asc2bn(BIGNUM **a, const char *str);
|
||||
//
|
||||
//go:linkname BNAsc2bn C.BN_asc2bn
|
||||
func BNAsc2bn(a **BIGNUM, str *c.Char) c.Int
|
||||
|
||||
/*
|
||||
BIGNUM *BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret);
|
||||
BIGNUM *BN_signed_bin2bn(const unsigned char *s, int len, BIGNUM *ret);
|
||||
int BN_bn2bin(const BIGNUM *a, unsigned char *to);
|
||||
int BN_bn2binpad(const BIGNUM *a, unsigned char *to, int tolen);
|
||||
int BN_signed_bn2bin(const BIGNUM *a, unsigned char *to, int tolen);
|
||||
BIGNUM *BN_lebin2bn(const unsigned char *s, int len, BIGNUM *ret);
|
||||
BIGNUM *BN_signed_lebin2bn(const unsigned char *s, int len, BIGNUM *ret);
|
||||
int BN_bn2lebinpad(const BIGNUM *a, unsigned char *to, int tolen);
|
||||
int BN_signed_bn2lebin(const BIGNUM *a, unsigned char *to, int tolen);
|
||||
BIGNUM *BN_native2bn(const unsigned char *s, int len, BIGNUM *ret);
|
||||
BIGNUM *BN_signed_native2bn(const unsigned char *s, int len, BIGNUM *ret);
|
||||
int BN_bn2nativepad(const BIGNUM *a, unsigned char *to, int tolen);
|
||||
int BN_signed_bn2native(const BIGNUM *a, unsigned char *to, int tolen);
|
||||
BIGNUM *BN_mpi2bn(const unsigned char *s, int len, BIGNUM *ret);
|
||||
int BN_bn2mpi(const BIGNUM *a, unsigned char *to);
|
||||
*/
|
||||
|
||||
// int BN_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b);
|
||||
//
|
||||
// llgo:link (*BIGNUM).Sub C.BN_sub
|
||||
func (*BIGNUM) Sub(a, b *BIGNUM) c.Int { return 0 }
|
||||
|
||||
// int BN_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b);
|
||||
//
|
||||
// llgo:link (*BIGNUM).Add C.BN_add
|
||||
func (*BIGNUM) Add(a, b *BIGNUM) c.Int { return 0 }
|
||||
|
||||
// int BN_usub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b);
|
||||
//
|
||||
// llgo:link (*BIGNUM).Usub C.BN_usub
|
||||
func (*BIGNUM) Usub(a, b *BIGNUM) c.Int { return 0 }
|
||||
|
||||
// int BN_uadd(BIGNUM *r, const BIGNUM *a, const BIGNUM *b);
|
||||
//
|
||||
// llgo:link (*BIGNUM).Uadd C.BN_uadd
|
||||
func (*BIGNUM) Uadd(a, b *BIGNUM) c.Int { return 0 }
|
||||
|
||||
// int BN_mul(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx);
|
||||
//
|
||||
// llgo:link (*BIGNUM).Mul C.BN_mul
|
||||
func (*BIGNUM) Mul(r, a, b *BIGNUM, ctx *BN_CTX) c.Int { return 0 }
|
||||
|
||||
// int BN_sqr(BIGNUM *r, const BIGNUM *a, BN_CTX *ctx);
|
||||
//
|
||||
// llgo:link (*BIGNUM).Sqr C.BN_sqr
|
||||
func (*BIGNUM) Sqr(r, a *BIGNUM, ctx *BN_CTX) c.Int { return 0 }
|
||||
|
||||
/** BN_set_negative sets sign of a BIGNUM
|
||||
* \param b pointer to the BIGNUM object
|
||||
* \param n 0 if the BIGNUM b should be positive and a value != 0 otherwise
|
||||
*/
|
||||
// void BN_set_negative(BIGNUM *b, int n);
|
||||
//
|
||||
// llgo:link (*BIGNUM).SetNegative C.BN_set_negative
|
||||
func (*BIGNUM) SetNegative(n c.Int) {}
|
||||
|
||||
/** BN_is_negative returns 1 if the BIGNUM is negative
|
||||
* \param b pointer to the BIGNUM object
|
||||
* \return 1 if a < 0 and 0 otherwise
|
||||
*/
|
||||
// int BN_is_negative(const BIGNUM *b);
|
||||
//
|
||||
// llgo:link (*BIGNUM).IsNegative C.BN_is_negative
|
||||
func (*BIGNUM) IsNegative() c.Int { return 0 }
|
||||
|
||||
// int BN_div(BIGNUM *dv, BIGNUM *rem, const BIGNUM *m, const BIGNUM *d, BN_CTX *ctx);
|
||||
//
|
||||
// llgo:link (*BIGNUM).Div C.BN_div
|
||||
func (*BIGNUM) Div(rem, m, d *BIGNUM, ctx *BN_CTX) c.Int { return 0 }
|
||||
|
||||
// int BN_nnmod(BIGNUM *r, const BIGNUM *m, const BIGNUM *d, BN_CTX *ctx);
|
||||
//
|
||||
// llgo:link (*BIGNUM).Nnmod C.BN_nnmod
|
||||
func (*BIGNUM) Nnmod(r, m, d *BIGNUM, ctx *BN_CTX) c.Int { return 0 }
|
||||
|
||||
// int BN_cmp(const BIGNUM *a, const BIGNUM *b);
|
||||
//
|
||||
// llgo:link (*BIGNUM).Cmp C.BN_cmp
|
||||
func (*BIGNUM) Cmp(b *BIGNUM) c.Int { return 0 }
|
||||
|
||||
// int BN_ucmp(const BIGNUM *a, const BIGNUM *b);
|
||||
//
|
||||
// llgo:link (*BIGNUM).Ucmp C.BN_ucmp
|
||||
func (*BIGNUM) Ucmp(b *BIGNUM) c.Int { return 0 }
|
||||
|
||||
// int BN_is_bit_set(const BIGNUM *a, int n);
|
||||
//
|
||||
// llgo:link (*BIGNUM).IsBitSet C.BN_is_bit_set
|
||||
func (*BIGNUM) IsBitSet(n c.Int) c.Int { return 0 }
|
||||
|
||||
// int BN_set_bit(BIGNUM *a, int n);
|
||||
//
|
||||
// llgo:link (*BIGNUM).SetBit C.BN_set_bit
|
||||
func (*BIGNUM) SetBit(n c.Int) c.Int { return 0 }
|
||||
|
||||
// int BN_clear_bit(BIGNUM *a, int n);
|
||||
//
|
||||
// llgo:link (*BIGNUM).ClearBit C.BN_clear_bit
|
||||
func (*BIGNUM) ClearBit(n c.Int) c.Int { return 0 }
|
||||
|
||||
// int BN_lshift(BIGNUM *r, const BIGNUM *a, int n);
|
||||
//
|
||||
// llgo:link (*BIGNUM).Lshift C.BN_lshift
|
||||
func (*BIGNUM) Lshift(a *BIGNUM, n c.Int) c.Int { return 0 }
|
||||
|
||||
// int BN_lshift1(BIGNUM *r, const BIGNUM *a);
|
||||
//
|
||||
// llgo:link (*BIGNUM).Lshift1 C.BN_lshift1
|
||||
func (*BIGNUM) Lshift1(a *BIGNUM) c.Int { return 0 }
|
||||
|
||||
// int BN_rshift(BIGNUM *r, const BIGNUM *a, int n);
|
||||
//
|
||||
// llgo:link (*BIGNUM).Rshift C.BN_rshift
|
||||
func (*BIGNUM) Rshift(a *BIGNUM, n c.Int) c.Int { return 0 }
|
||||
|
||||
// int BN_rshift1(BIGNUM *r, const BIGNUM *a);
|
||||
//
|
||||
// llgo:link (*BIGNUM).Rshift1 C.BN_rshift1
|
||||
func (*BIGNUM) Rshift1(a *BIGNUM) c.Int { return 0 }
|
||||
|
||||
// int BN_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, BN_CTX *ctx);
|
||||
//
|
||||
// llgo:link (*BIGNUM).Exp C.BN_exp
|
||||
func (*BIGNUM) Exp(a, p *BIGNUM, ctx *BN_CTX) c.Int { return 0 }
|
||||
|
||||
// int BN_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, const BIGNUM *m, BN_CTX *ctx);
|
||||
//
|
||||
// llgo:link (*BIGNUM).ModExp C.BN_mod_exp
|
||||
func (*BIGNUM) ModExp(a, p, m *BIGNUM, ctx *BN_CTX) c.Int { return 0 }
|
||||
|
||||
// int BN_gcd(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx);
|
||||
//
|
||||
// llgo:link (*BIGNUM).Gcd C.BN_gcd
|
||||
func (*BIGNUM) Gcd(a, b *BIGNUM, ctx *BN_CTX) c.Int { return 0 }
|
||||
|
||||
// int BN_are_coprime(const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx);
|
||||
//
|
||||
// llgo:link (*BIGNUM).AreCoprime C.BN_are_coprime
|
||||
func (*BIGNUM) AreCoprime(b *BIGNUM, ctx *BN_CTX) c.Int { return 0 }
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type BN_GENCB struct {
|
||||
Unused [0]byte
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
156
c/openssl/err.go
Normal file
156
c/openssl/err.go
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* 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 openssl
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/goplus/llgo/c"
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/*-
|
||||
* The error code packs differently depending on if it records a system
|
||||
* error or an OpenSSL error.
|
||||
*
|
||||
* A system error packs like this (we follow POSIX and only allow positive
|
||||
* numbers that fit in an |int|):
|
||||
*
|
||||
* +-+-------------------------------------------------------------+
|
||||
* |1| system error number |
|
||||
* +-+-------------------------------------------------------------+
|
||||
*
|
||||
* An OpenSSL error packs like this:
|
||||
*
|
||||
* <---------------------------- 32 bits -------------------------->
|
||||
* <--- 8 bits ---><------------------ 23 bits ----------------->
|
||||
* +-+---------------+---------------------------------------------+
|
||||
* |0| library | reason |
|
||||
* +-+---------------+---------------------------------------------+
|
||||
*
|
||||
* A few of the reason bits are reserved as flags with special meaning:
|
||||
*
|
||||
* <5 bits-<>--------- 19 bits ----------------->
|
||||
* +-------+-+-----------------------------------+
|
||||
* | rflags| | reason |
|
||||
* +-------+-+-----------------------------------+
|
||||
* ^
|
||||
* |
|
||||
* ERR_RFLAG_FATAL = ERR_R_FATAL
|
||||
*
|
||||
* The reason flags are part of the overall reason code for practical
|
||||
* reasons, as they provide an easy way to place different types of
|
||||
* reason codes in different numeric ranges.
|
||||
*
|
||||
* The currently known reason flags are:
|
||||
*
|
||||
* ERR_RFLAG_FATAL Flags that the reason code is considered fatal.
|
||||
* For backward compatibility reasons, this flag
|
||||
* is also the code for ERR_R_FATAL (that reason
|
||||
* code served the dual purpose of flag and reason
|
||||
* code in one in pre-3.0 OpenSSL).
|
||||
* ERR_RFLAG_COMMON Flags that the reason code is common to all
|
||||
* libraries. All ERR_R_ macros must use this flag,
|
||||
* and no other _R_ macro is allowed to use it.
|
||||
*/
|
||||
type Errno c.Ulong
|
||||
|
||||
// ERR_get_error returns the earliest error code from the thread's error queue and
|
||||
// removes the entry. This function can be called repeatedly until there are no more
|
||||
// error codes to return.
|
||||
//
|
||||
// unsigned long ERR_get_error(void);
|
||||
//
|
||||
//go:linkname ERRGetError C.ERR_get_error
|
||||
func ERRGetError() Errno
|
||||
|
||||
// ERR_get_error_all() is the same as ERR_get_error(), but on success it additionally
|
||||
// stores the filename, line number and function where the error occurred in *file,
|
||||
// *line and *func, and also extra text and flags in *data, *flags. If any of those
|
||||
// parameters are NULL, it will not be changed.
|
||||
//
|
||||
// unsigned long ERR_get_error_all(
|
||||
// const char **file, int *line, const char **func, const char **data, int *flags);
|
||||
//
|
||||
//go:linkname ERRGetErrorAll C.ERR_get_error_all
|
||||
func ERRGetErrorAll(
|
||||
file **c.Char, line *c.Int, function **c.Char, data **c.Char, flags *c.Int) Errno
|
||||
|
||||
// unsigned long ERR_peek_error(void);
|
||||
//
|
||||
//go:linkname ERRPeekError C.ERR_peek_error
|
||||
func ERRPeekError() Errno
|
||||
|
||||
// unsigned long ERR_peek_error_all(
|
||||
// const char **file, int *line, const char **func, const char **data, int *flags);
|
||||
//
|
||||
//go:linkname ERRPeekErrorAll C.ERR_peek_error_all
|
||||
func ERRPeekErrorAll(
|
||||
file **c.Char, line *c.Int, function **c.Char, data **c.Char, flags *c.Int) Errno
|
||||
|
||||
// unsigned long ERR_peek_last_error(void);
|
||||
//
|
||||
//go:linkname ERRPeekLastError C.ERR_peek_last_error
|
||||
func ERRPeekLastError() Errno
|
||||
|
||||
// unsigned long ERR_peek_last_error_all(
|
||||
// const char **file, int *line, const char **func, const char **data, int *flags);
|
||||
//
|
||||
//go:linkname ERRPeekLastErrorAll C.ERR_peek_last_error_all
|
||||
func ERRPeekLastErrorAll(
|
||||
file **c.Char, line *c.Int, function **c.Char, data **c.Char, flags *c.Int) Errno
|
||||
|
||||
// void ERR_clear_error(void);
|
||||
//
|
||||
//go:linkname ERRClearError C.ERR_clear_error
|
||||
func ERRClearError()
|
||||
|
||||
// ERR_error_string() generates a human-readable string representing the error code e,
|
||||
// and places it at buf. buf must be at least 256 bytes long.
|
||||
//
|
||||
// char *ERR_error_string(unsigned long e, char *buf);
|
||||
//
|
||||
//go:linkname ERRErrorString C.ERR_error_string
|
||||
func ERRErrorString(e Errno, buf *c.Char) *c.Char
|
||||
|
||||
// ERR_lib_error_string() and ERR_reason_error_string() return the library name and
|
||||
// reason string respectively.
|
||||
//
|
||||
// const char *ERR_lib_error_string(unsigned long e);
|
||||
//
|
||||
//go:linkname ERRLibErrorString C.ERR_lib_error_string
|
||||
func ERRLibErrorString(e Errno) *c.Char
|
||||
|
||||
// const char *ERR_reason_error_string(unsigned long e);
|
||||
//
|
||||
//go:linkname ERRReasonErrorString C.ERR_reason_error_string
|
||||
func ERRReasonErrorString(e Errno) *c.Char
|
||||
|
||||
// void ERR_print_errors_cb(int (*cb) (const char *str, size_t len, void *u), void *u);
|
||||
//
|
||||
// [pid]:error:[error code]:[library name]:[function name]:[reason string]:[filename]:[line]:[optional text message]
|
||||
//
|
||||
//go:linkname ERRPrintErrorsCb C.ERR_print_errors_cb
|
||||
func ERRPrintErrorsCb(cb func(str *c.Char, len uintptr, u unsafe.Pointer) c.Int, u unsafe.Pointer)
|
||||
|
||||
// void ERR_print_errors_fp(FILE *fp);
|
||||
//
|
||||
//go:linkname ERRPrintErrorsFp C.ERR_print_errors_fp
|
||||
func ERRPrintErrorsFp(fp c.FilePtr)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
121
c/openssl/hmac.go
Normal file
121
c/openssl/hmac.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package openssl
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/goplus/llgo/c"
|
||||
)
|
||||
|
||||
const EVP_MAX_MD_SIZE = 64 /* longest known is SHA512 */
|
||||
|
||||
// const EVP_MD *EVP_sha1(void)
|
||||
//
|
||||
//go:linkname EVP_sha1 C.EVP_sha1
|
||||
func EVP_sha1() *EVP_MD
|
||||
|
||||
// const EVP_MD *EVP_sha224(void)
|
||||
//
|
||||
//go:linkname EVP_sha224 C.EVP_sha224
|
||||
func EVP_sha224() *EVP_MD
|
||||
|
||||
// func EVP_sha256() *EVP_MD
|
||||
//
|
||||
//go:linkname EVP_sha256 C.EVP_sha256
|
||||
func EVP_sha256() *EVP_MD
|
||||
|
||||
// const EVP_MD *EVP_sha512_224(void)
|
||||
//
|
||||
//go:linkname EVP_sha512_224 C.EVP_sha512_224
|
||||
func EVP_sha512_224() *EVP_MD
|
||||
|
||||
// const EVP_MD *EVP_sha512_256(void)
|
||||
//
|
||||
//go:linkname EVP_sha512_256 C.EVP_sha512_256
|
||||
func EVP_sha512_256() *EVP_MD
|
||||
|
||||
// const EVP_MD *EVP_sha384(void)
|
||||
//
|
||||
//go:linkname EVP_sha384 C.EVP_sha384
|
||||
func EVP_sha384() *EVP_MD
|
||||
|
||||
// const EVP_MD *EVP_sha512(void)
|
||||
//
|
||||
//go:linkname EVP_sha512 C.EVP_sha512
|
||||
func EVP_sha512() *EVP_MD
|
||||
|
||||
type EVP_MD struct {
|
||||
Unused [0]byte
|
||||
}
|
||||
|
||||
type EVP_MD_CTX struct {
|
||||
Unused [0]byte
|
||||
}
|
||||
|
||||
type HMAC_CTX struct {
|
||||
Unused [0]byte
|
||||
}
|
||||
|
||||
// OSSL_DEPRECATEDIN_3_0 HMAC_CTX *HMAC_CTX_new(void);
|
||||
//
|
||||
//go:linkname NewHMAC_CTX C.HMAC_CTX_new
|
||||
func NewHMAC_CTX() *HMAC_CTX
|
||||
|
||||
// OSSL_DEPRECATEDIN_3_0 void HMAC_CTX_free(HMAC_CTX *ctx);
|
||||
//
|
||||
// llgo:link (*HMAC_CTX).Free C.HMAC_CTX_free
|
||||
func (c *HMAC_CTX) Free() {}
|
||||
|
||||
// OSSL_DEPRECATEDIN_3_0 size_t HMAC_size(const HMAC_CTX *e);
|
||||
//
|
||||
// llgo:link (*HMAC_CTX).Size C.HMAC_size
|
||||
func (c *HMAC_CTX) Size() uintptr { return 0 }
|
||||
|
||||
// OSSL_DEPRECATEDIN_3_0 int HMAC_CTX_reset(HMAC_CTX *ctx);
|
||||
//
|
||||
// llgo:link (*HMAC_CTX).Reset C.HMAC_CTX_reset
|
||||
func (c *HMAC_CTX) Reset() c.Int { return 0 }
|
||||
|
||||
// OSSL_DEPRECATEDIN_1_1_0 __owur int HMAC_Init(HMAC_CTX *ctx,
|
||||
// const void *key, int len,
|
||||
// const EVP_MD *md);
|
||||
//
|
||||
// llgo:link (*HMAC_CTX).Init C.HMAC_Init
|
||||
func (c *HMAC_CTX) Init(key unsafe.Pointer, len c.Int, md *EVP_MD) c.Int { return 0 }
|
||||
|
||||
// OSSL_DEPRECATEDIN_3_0 int HMAC_Init_ex(HMAC_CTX *ctx, const void *key, int len,
|
||||
// const EVP_MD *md, ENGINE *impl);
|
||||
//
|
||||
// llgo:link (*HMAC_CTX).InitEx C.HMAC_Init_ex
|
||||
func (c *HMAC_CTX) InitEx(key unsafe.Pointer, len c.Int, md *EVP_MD, impl unsafe.Pointer) c.Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// OSSL_DEPRECATEDIN_3_0 int HMAC_Update(HMAC_CTX *ctx, const unsigned char *data,
|
||||
// size_t len);
|
||||
//
|
||||
// llgo:link (*HMAC_CTX).Update C.HMAC_Update
|
||||
func (c *HMAC_CTX) Update(data unsafe.Pointer, len uintptr) c.Int { return 0 }
|
||||
|
||||
func (c *HMAC_CTX) UpdateBytes(data []byte) c.Int {
|
||||
return c.Update(unsafe.Pointer(unsafe.SliceData(data)), uintptr(len(data)))
|
||||
}
|
||||
|
||||
func (c *HMAC_CTX) UpdateString(data string) c.Int {
|
||||
return c.Update(unsafe.Pointer(unsafe.StringData(data)), uintptr(len(data)))
|
||||
}
|
||||
|
||||
// OSSL_DEPRECATEDIN_3_0 int HMAC_Final(HMAC_CTX *ctx, unsigned char *md,
|
||||
// unsigned int *len);
|
||||
//
|
||||
// llgo:link (*HMAC_CTX).Final C.HMAC_Final
|
||||
func (c *HMAC_CTX) Final(md *byte, len *c.Uint) c.Int { return 0 }
|
||||
|
||||
// OSSL_DEPRECATEDIN_3_0 __owur int HMAC_CTX_copy(HMAC_CTX *dctx, HMAC_CTX *sctx);
|
||||
//
|
||||
// llgo:link (*HMAC_CTX).Copy C.HMAC_CTX_copy
|
||||
func (c *HMAC_CTX) Copy(sctx *HMAC_CTX) c.Int { return 0 }
|
||||
|
||||
// OSSL_DEPRECATEDIN_3_0 void HMAC_CTX_set_flags(HMAC_CTX *ctx, unsigned long flags);
|
||||
//
|
||||
// llgo:link (*HMAC_CTX).SetFlags C.HMAC_CTX_set_flags
|
||||
func (c *HMAC_CTX) SetFlags(flags c.Ulong) {}
|
||||
@@ -16,8 +16,23 @@
|
||||
|
||||
package openssl
|
||||
|
||||
const (
|
||||
LLGoPackage = "link: $(pkg-config --libs openssl); -lssl -lcrypto"
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/goplus/llgo/c"
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const (
|
||||
LLGoFiles = "$(pkg-config --cflags openssl): _wrap/openssl.c"
|
||||
LLGoPackage = "link: $(pkg-config --libs openssl); -lssl -lcrypto"
|
||||
)
|
||||
|
||||
//go:linkname Free C.opensslFree
|
||||
func Free(ptr unsafe.Pointer)
|
||||
|
||||
//go:linkname FreeCStr C.opensslFree
|
||||
func FreeCStr(ptr *c.Char)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
37
c/openssl/pem.go
Normal file
37
c/openssl/pem.go
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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 openssl
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/goplus/llgo/c"
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// typedef int (*pem_password_cb)(char *buf, int size, int rwflag, void *userdata);
|
||||
//
|
||||
// llgo:type C
|
||||
type PemPasswordCb func(buf *c.Char, size, rwflag c.Int, userdata unsafe.Pointer) c.Int
|
||||
|
||||
// RSA *PEM_read_bio_RSAPrivateKey(BIO *bp, RSA **x, pem_password_cb *cb, void *u);
|
||||
//
|
||||
//go:linkname PEMReadBioRSAPrivateKey C.PEM_read_bio_RSAPrivateKey
|
||||
func PEMReadBioRSAPrivateKey(bp *BIO, x **RSA, cb PemPasswordCb, u unsafe.Pointer) *RSA
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
80
c/openssl/rand.go
Normal file
80
c/openssl/rand.go
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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 openssl
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/goplus/llgo/c"
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// int RAND_bytes(unsigned char *buf, int num);
|
||||
//
|
||||
//go:linkname RANDBufferWithLen C.RAND_bytes
|
||||
func RANDBufferWithLen(buf *byte, num c.Int) c.Int
|
||||
|
||||
func RANDBytes(buf []byte) c.Int {
|
||||
return RANDBufferWithLen(unsafe.SliceData(buf), c.Int(len(buf)))
|
||||
}
|
||||
|
||||
// int RAND_priv_bytes(unsigned char *buf, int num);
|
||||
//
|
||||
//go:linkname RANDPrivBufferWithLen C.RAND_priv_bytes
|
||||
func RANDPrivBufferWithLen(buf *byte, num c.Int) c.Int
|
||||
|
||||
func RANDPrivBytes(buf []byte) c.Int {
|
||||
return RANDPrivBufferWithLen(unsafe.SliceData(buf), c.Int(len(buf)))
|
||||
}
|
||||
|
||||
// void RAND_seed(const void *buf, int num);
|
||||
//
|
||||
//go:linkname RANDSeed C.RAND_seed
|
||||
func RANDSeed(buf unsafe.Pointer, num c.Int)
|
||||
|
||||
// void RAND_keep_random_devices_open(int keep);
|
||||
//
|
||||
//go:linkname RANDKeepRandomDevicesOpen C.RAND_keep_random_devices_open
|
||||
func RANDKeepRandomDevicesOpen(keep c.Int)
|
||||
|
||||
// int RAND_load_file(const char *file, long max_bytes);
|
||||
//
|
||||
//go:linkname RANDLoadFile C.RAND_load_file
|
||||
func RANDLoadFile(file *c.Char, maxBytes c.Long) c.Int
|
||||
|
||||
// int RAND_write_file(const char *file);
|
||||
//
|
||||
//go:linkname RANDWriteFile C.RAND_write_file
|
||||
func RANDWriteFile(file *c.Char) c.Int
|
||||
|
||||
// const char *RAND_file_name(char *file, size_t num);
|
||||
//
|
||||
//go:linkname RANDFileName C.RAND_file_name
|
||||
func RANDFileName(file *c.Char, num uintptr) *c.Char
|
||||
|
||||
// int RAND_status(void);
|
||||
//
|
||||
//go:linkname RANDStatus C.RAND_status
|
||||
func RANDStatus() c.Int
|
||||
|
||||
// int RAND_poll(void);
|
||||
//
|
||||
//go:linkname RANDPoll C.RAND_poll
|
||||
func RANDPoll() c.Int
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
305
c/openssl/rsa.go
Normal file
305
c/openssl/rsa.go
Normal file
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
* 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 openssl
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/goplus/llgo/c"
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type RSA_METHOD struct {
|
||||
Unused [0]byte
|
||||
}
|
||||
|
||||
// OSSL_DEPRECATEDIN_3_0 RSA_METHOD *RSA_meth_new(const char *name, int flags);
|
||||
//
|
||||
//go:linkname RSAMethNew C.RSA_meth_new
|
||||
func RSAMethNew(name *byte, flags c.Int) *RSA_METHOD
|
||||
|
||||
// OSSL_DEPRECATEDIN_3_0 void RSA_meth_free(RSA_METHOD *meth);
|
||||
//
|
||||
// llgo:link (*RSA_METHOD).Free C.RSA_meth_free
|
||||
func (*RSA_METHOD) Free() {}
|
||||
|
||||
// OSSL_DEPRECATEDIN_3_0
|
||||
// int RSA_meth_set_init(RSA_METHOD *rsa, int (*init) (RSA *rsa));
|
||||
//
|
||||
// llgo:link (*RSA_METHOD).SetInit C.RSA_meth_set_init
|
||||
func (*RSA_METHOD) SetInit(init func(rsa *RSA) c.Int) c.Int { return 0 }
|
||||
|
||||
// OSSL_DEPRECATEDIN_3_0
|
||||
// int RSA_meth_set_finish(RSA_METHOD *rsa, int (*finish) (RSA *rsa));
|
||||
//
|
||||
// llgo:link (*RSA_METHOD).SetFinish C.RSA_meth_set_finish
|
||||
func (*RSA_METHOD) SetFinish(finish func(rsa *RSA) c.Int) c.Int { return 0 }
|
||||
|
||||
/*
|
||||
OSSL_DEPRECATEDIN_3_0
|
||||
int RSA_meth_set_mod_exp(RSA_METHOD *rsa,
|
||||
int (*mod_exp) (BIGNUM *r0, const BIGNUM *i, RSA *rsa,
|
||||
BN_CTX *ctx));
|
||||
*/
|
||||
// llgo:link (*RSA_METHOD).SetModExp C.RSA_meth_set_mod_exp
|
||||
func (*RSA_METHOD) SetModExp(modExp func(
|
||||
r0 *BIGNUM, i *BIGNUM, rsa *RSA, ctx *BN_CTX) c.Int) c.Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
/*
|
||||
OSSL_DEPRECATEDIN_3_0
|
||||
int RSA_meth_set_bn_mod_exp(RSA_METHOD *rsa,
|
||||
int (*bn_mod_exp) (BIGNUM *r,
|
||||
const BIGNUM *a,
|
||||
const BIGNUM *p,
|
||||
const BIGNUM *m,
|
||||
BN_CTX *ctx,
|
||||
BN_MONT_CTX *m_ctx));
|
||||
//-llgo:link (*RSA_METHOD).SetBnModExp C.RSA_meth_set_bn_mod_exp
|
||||
func (*RSA_METHOD) SetBnModExp(bnModExp func(
|
||||
r *BIGNUM, a *BIGNUM, p *BIGNUM, m *BIGNUM, ctx *BN_CTX, mCtx *BN_MONT_CTX) c.Int) c.Int {
|
||||
return 0
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
OSSL_DEPRECATEDIN_3_0
|
||||
int RSA_meth_set_pub_enc(RSA_METHOD *rsa,
|
||||
int (*pub_enc) (int flen, const unsigned char *from,
|
||||
unsigned char *to, RSA *rsa,
|
||||
int padding));
|
||||
*/
|
||||
// llgo:link (*RSA_METHOD).SetPubEnc C.RSA_meth_set_pub_enc
|
||||
func (*RSA_METHOD) SetPubEnc(pubEnc func(
|
||||
flen c.Int, from *byte, to *byte, rsa *RSA, padding c.Int) c.Int) c.Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
/*
|
||||
OSSL_DEPRECATEDIN_3_0
|
||||
int RSA_meth_set_pub_dec(RSA_METHOD *rsa,
|
||||
int (*pub_dec) (int flen, const unsigned char *from,
|
||||
unsigned char *to, RSA *rsa,
|
||||
int padding));
|
||||
*/
|
||||
// llgo:link (*RSA_METHOD).SetPubDec C.RSA_meth_set_pub_dec
|
||||
func (*RSA_METHOD) SetPubDec(pubDec func(
|
||||
flen c.Int, from *byte, to *byte, rsa *RSA, padding c.Int) c.Int) c.Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
/*
|
||||
OSSL_DEPRECATEDIN_3_0
|
||||
int RSA_meth_set_priv_enc(RSA_METHOD *rsa,
|
||||
int (*priv_enc) (int flen, const unsigned char *from,
|
||||
unsigned char *to, RSA *rsa,
|
||||
int padding));
|
||||
*/
|
||||
// llgo:link (*RSA_METHOD).SetPrivEnc C.RSA_meth_set_priv_enc
|
||||
func (*RSA_METHOD) SetPrivEnc(privEnc func(
|
||||
flen c.Int, from *byte, to *byte, rsa *RSA, padding c.Int) c.Int) c.Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
/*
|
||||
OSSL_DEPRECATEDIN_3_0
|
||||
int RSA_meth_set_priv_dec(RSA_METHOD *rsa,
|
||||
int (*priv_dec) (int flen, const unsigned char *from,
|
||||
unsigned char *to, RSA *rsa,
|
||||
int padding));
|
||||
*/
|
||||
// llgo:link (*RSA_METHOD).SetPrivDec C.RSA_meth_set_priv_dec
|
||||
func (*RSA_METHOD) SetPrivDec(privDec func(
|
||||
flen c.Int, from *byte, to *byte, rsa *RSA, padding c.Int) c.Int) c.Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
/*
|
||||
OSSL_DEPRECATEDIN_3_0
|
||||
int RSA_meth_set_sign(RSA_METHOD *rsa,
|
||||
int (*sign) (int type, const unsigned char *m,
|
||||
unsigned int m_length,
|
||||
unsigned char *sigret, unsigned int *siglen,
|
||||
const RSA *rsa));
|
||||
*/
|
||||
// llgo:link (*RSA_METHOD).SetSign C.RSA_meth_set_sign
|
||||
func (*RSA_METHOD) SetSign(sign func(
|
||||
typ c.Int, msg *byte, mlen c.Uint,
|
||||
sigret *byte, siglen *c.Uint, rsa *RSA) c.Int) c.Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
/*
|
||||
OSSL_DEPRECATEDIN_3_0
|
||||
int RSA_meth_set_verify(RSA_METHOD *rsa,
|
||||
int (*verify) (int dtype, const unsigned char *m,
|
||||
unsigned int m_length,
|
||||
const unsigned char *sigbuf,
|
||||
unsigned int siglen, const RSA *rsa));
|
||||
*/
|
||||
// llgo:link (*RSA_METHOD).SetVerify C.RSA_meth_set_verify
|
||||
func (*RSA_METHOD) SetVerify(verify func(
|
||||
dtype c.Int, msg *byte, mlen c.Uint,
|
||||
sigbuf *byte, siglen c.Uint, rsa *RSA) c.Int) c.Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
/*
|
||||
OSSL_DEPRECATEDIN_3_0
|
||||
int RSA_meth_set_keygen(RSA_METHOD *rsa,
|
||||
int (*keygen) (RSA *rsa, int bits, BIGNUM *e,
|
||||
BN_GENCB *cb));
|
||||
|
||||
OSSL_DEPRECATEDIN_3_0
|
||||
int RSA_meth_set_multi_prime_keygen(RSA_METHOD *meth,
|
||||
int (*keygen) (RSA *rsa, int bits,
|
||||
int primes, BIGNUM *e,
|
||||
BN_GENCB *cb));
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type RSA struct {
|
||||
Unused [0]byte
|
||||
}
|
||||
|
||||
// OSSL_DEPRECATEDIN_3_0 RSA *RSA_new(void);
|
||||
//
|
||||
//go:linkname RSANew C.RSA_new
|
||||
func RSANew() *RSA
|
||||
|
||||
// OSSL_DEPRECATEDIN_3_0 RSA *RSA_new_method(ENGINE *engine);
|
||||
|
||||
// OSSL_DEPRECATEDIN_3_0 void RSA_free(RSA *r);
|
||||
//
|
||||
// llgo:link (*RSA).Free C.RSA_free
|
||||
func (*RSA) Free() {}
|
||||
|
||||
// "up" the RSA object's reference count
|
||||
// OSSL_DEPRECATEDIN_3_0 int RSA_up_ref(RSA *r);
|
||||
//
|
||||
// llgo:link (*RSA).UpRef C.RSA_up_ref
|
||||
func (*RSA) UpRef() c.Int { return 0 }
|
||||
|
||||
// OSSL_DEPRECATEDIN_3_0 int RSA_bits(const RSA *rsa);
|
||||
//
|
||||
// llgo:link (*RSA).Bits C.RSA_bits
|
||||
func (*RSA) Bits() c.Int { return 0 }
|
||||
|
||||
// OSSL_DEPRECATEDIN_3_0 int RSA_size(const RSA *rsa);
|
||||
//
|
||||
// llgo:link (*RSA).Size C.RSA_size
|
||||
func (*RSA) Size() c.Int { return 0 }
|
||||
|
||||
// OSSL_DEPRECATEDIN_3_0 int RSA_security_bits(const RSA *rsa);
|
||||
//
|
||||
// llgo:link (*RSA).SecurityBits C.RSA_security_bits
|
||||
func (*RSA) SecurityBits() c.Int { return 0 }
|
||||
|
||||
// OSSL_DEPRECATEDIN_3_0 int RSA_flags(const RSA *r);
|
||||
//
|
||||
// llgo:link (*RSA).Flags C.RSA_flags
|
||||
func (*RSA) Flags() c.Int { return 0 }
|
||||
|
||||
// OSSL_DEPRECATEDIN_3_0 void RSA_set_flags(RSA *r, int flags);
|
||||
//
|
||||
// llgo:link (*RSA).SetFlags C.RSA_set_flags
|
||||
func (*RSA) SetFlags(flags c.Int) {}
|
||||
|
||||
// OSSL_DEPRECATEDIN_3_0 void RSA_clear_flags(RSA *r, int flags);
|
||||
//
|
||||
// llgo:link (*RSA).ClearFlags C.RSA_clear_flags
|
||||
func (*RSA) ClearFlags(flags c.Int) {}
|
||||
|
||||
// OSSL_DEPRECATEDIN_3_0 int RSA_test_flags(const RSA *r, int flags);
|
||||
//
|
||||
// llgo:link (*RSA).TestFlags C.RSA_test_flags
|
||||
func (*RSA) TestFlags(flags c.Int) c.Int { return 0 }
|
||||
|
||||
// OSSL_DEPRECATEDIN_3_0 int RSA_get_version(RSA *r);
|
||||
//
|
||||
// llgo:link (*RSA).GetVersion C.RSA_get_version
|
||||
func (*RSA) GetVersion() c.Int { return 0 }
|
||||
|
||||
// OSSL_DEPRECATEDIN_3_0 int RSA_set_ex_data(RSA *r, int idx, void *arg);
|
||||
//
|
||||
// llgo:link (*RSA).SetExData C.RSA_set_ex_data
|
||||
func (*RSA) SetExData(idx c.Int, arg unsafe.Pointer) c.Int { return 0 }
|
||||
|
||||
// OSSL_DEPRECATEDIN_3_0 void *RSA_get_ex_data(const RSA *r, int idx);
|
||||
//
|
||||
// llgo:link (*RSA).GetExData C.RSA_get_ex_data
|
||||
func (*RSA) GetExData(idx c.Int) unsafe.Pointer { return nil }
|
||||
|
||||
// OSSL_DEPRECATEDIN_3_0 int RSA_set_method(RSA *rsa, const RSA_METHOD *meth);
|
||||
//
|
||||
// llgo:link (*RSA).SetMethod C.RSA_set_method
|
||||
func (*RSA) SetMethod(meth *RSA_METHOD) c.Int { return 0 }
|
||||
|
||||
// OSSL_DEPRECATEDIN_3_0 int RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb);
|
||||
//
|
||||
// llgo:link (*RSA).GenerateKeyEx C.RSA_generate_key_ex
|
||||
func (*RSA) GenerateKeyEx(bits c.Int, e *BIGNUM, cb *BN_GENCB) c.Int { return 0 }
|
||||
|
||||
// OSSL_DEPRECATEDIN_3_0 int RSA_generate_multi_prime_key(RSA *rsa, int bits, int primes, BIGNUM *e, BN_GENCB *cb);
|
||||
//
|
||||
// llgo:link (*RSA).GenerateMultiPrimeKey C.RSA_generate_multi_prime_key
|
||||
func (*RSA) GenerateMultiPrimeKey(bits, primes c.Int, e *BIGNUM, cb *BN_GENCB) c.Int { return 0 }
|
||||
|
||||
/*
|
||||
// next 4 return -1 on error
|
||||
|
||||
OSSL_DEPRECATEDIN_3_0
|
||||
int RSA_public_encrypt(int flen, const unsigned char *from, unsigned char *to,
|
||||
RSA *rsa, int padding);
|
||||
OSSL_DEPRECATEDIN_3_0
|
||||
int RSA_private_encrypt(int flen, const unsigned char *from, unsigned char *to,
|
||||
RSA *rsa, int padding);
|
||||
OSSL_DEPRECATEDIN_3_0
|
||||
int RSA_public_decrypt(int flen, const unsigned char *from, unsigned char *to,
|
||||
RSA *rsa, int padding);
|
||||
OSSL_DEPRECATEDIN_3_0
|
||||
int RSA_private_decrypt(int flen, const unsigned char *from, unsigned char *to,
|
||||
RSA *rsa, int padding);
|
||||
*/
|
||||
//go:linkname RSAPublicEncrypt C.RSA_public_encrypt
|
||||
func RSAPublicEncrypt(flen c.Int, from *byte, to *byte, rsa *RSA, padding c.Int) c.Int
|
||||
|
||||
//go:linkname RSAPrivateEncrypt C.RSA_private_encrypt
|
||||
func RSAPrivateEncrypt(flen c.Int, from *byte, to *byte, rsa *RSA, padding c.Int) c.Int
|
||||
|
||||
//go:linkname RSAPublicDecrypt C.RSA_public_decrypt
|
||||
func RSAPublicDecrypt(flen c.Int, from *byte, to *byte, rsa *RSA, padding c.Int) c.Int
|
||||
|
||||
//go:linkname RSAPrivateDecrypt C.RSA_private_decrypt
|
||||
func RSAPrivateDecrypt(flen c.Int, from *byte, to *byte, rsa *RSA, padding c.Int) c.Int
|
||||
|
||||
// OSSL_DEPRECATEDIN_3_0 int RSA_sign(
|
||||
// int type, const unsigned char *m, unsigned int m_length,
|
||||
// unsigned char *sigret, unsigned int *siglen, RSA *rsa);
|
||||
//
|
||||
//go:linkname RSASign C.RSA_sign
|
||||
func RSASign(typ c.Int, msg *byte, mlen c.Uint, sigret *byte, siglen *c.Uint, rsa *RSA) c.Int
|
||||
|
||||
// OSSL_DEPRECATEDIN_3_0 int RSA_verify(int type, const unsigned char *m,
|
||||
// unsigned int m_length,
|
||||
// const unsigned char *sigbuf,
|
||||
// unsigned int siglen, RSA *rsa);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
9
c/os/_os/os.c
Normal file
9
c/os/_os/os.c
Normal file
@@ -0,0 +1,9 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
int llgoClearenv() {
|
||||
extern char **environ;
|
||||
if (environ != NULL) {
|
||||
*environ = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -27,10 +27,6 @@ import (
|
||||
"github.com/goplus/llgo/c/syscall"
|
||||
)
|
||||
|
||||
const (
|
||||
LLGoPackage = "decl"
|
||||
)
|
||||
|
||||
const (
|
||||
PATH_MAX = C.PATH_MAX
|
||||
)
|
||||
@@ -150,9 +146,6 @@ func Putenv(env *c.Char) c.Int
|
||||
//go:linkname Unsetenv C.unsetenv
|
||||
func Unsetenv(name *c.Char) c.Int
|
||||
|
||||
//go:linkname Clearenv C.clearenv
|
||||
func Clearenv()
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
//go:linkname Fchdir C.fchdir
|
||||
|
||||
28
c/os/os_linux.go
Normal file
28
c/os/os_linux.go
Normal file
@@ -0,0 +1,28 @@
|
||||
//go:build linux
|
||||
|
||||
/*
|
||||
* 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 os
|
||||
|
||||
import "C"
|
||||
|
||||
const (
|
||||
LLGoPackage = "decl"
|
||||
)
|
||||
|
||||
//go:linkname Clearenv C.clearenv
|
||||
func Clearenv()
|
||||
29
c/os/os_other.go
Normal file
29
c/os/os_other.go
Normal file
@@ -0,0 +1,29 @@
|
||||
//go:build !linux
|
||||
|
||||
/*
|
||||
* 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 os
|
||||
|
||||
import "C"
|
||||
|
||||
const (
|
||||
LLGoFiles = "_os/os.c"
|
||||
LLGoPackage = "link"
|
||||
)
|
||||
|
||||
//go:linkname Clearenv C.llgoClearenv
|
||||
func Clearenv()
|
||||
268
chore/llcppg/ast/ast.go
Normal file
268
chore/llcppg/ast/ast.go
Normal file
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
* 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 ast
|
||||
|
||||
// =============================================================================
|
||||
|
||||
type Node interface {
|
||||
}
|
||||
|
||||
type Expr interface {
|
||||
Node
|
||||
exprNode()
|
||||
}
|
||||
|
||||
type Decl interface {
|
||||
Node
|
||||
declNode()
|
||||
}
|
||||
|
||||
type Stmt interface {
|
||||
Node
|
||||
stmtNode()
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Expressions (Types are also expressions)
|
||||
|
||||
type TypeKind uint
|
||||
|
||||
const (
|
||||
Int TypeKind = iota
|
||||
Char
|
||||
Float
|
||||
Complex
|
||||
Bool
|
||||
)
|
||||
|
||||
type TypeFlag uint
|
||||
|
||||
const (
|
||||
Signed TypeFlag = 1 << iota
|
||||
Unsigned
|
||||
Long
|
||||
LongLong
|
||||
Double
|
||||
Short
|
||||
)
|
||||
|
||||
// [signed/unsigned/short/long/long long/double] [int]/char/float/complex/bool
|
||||
type BuiltinType struct {
|
||||
Kind TypeKind
|
||||
Flags TypeFlag
|
||||
}
|
||||
|
||||
func (*BuiltinType) exprNode() {}
|
||||
|
||||
// ------------------------------------------------
|
||||
|
||||
// Name
|
||||
type Ident struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (*Ident) exprNode() {}
|
||||
|
||||
// ------------------------------------------------
|
||||
|
||||
type Tag int
|
||||
|
||||
const (
|
||||
Struct Tag = iota
|
||||
Union
|
||||
Enum
|
||||
Class
|
||||
)
|
||||
|
||||
// struct/union/enum/class Name
|
||||
type TagExpr struct {
|
||||
Tag Tag
|
||||
Name *Ident
|
||||
}
|
||||
|
||||
func (*TagExpr) exprNode() {}
|
||||
|
||||
// ------------------------------------------------
|
||||
|
||||
// (X)
|
||||
type ParenExpr struct {
|
||||
X Expr
|
||||
}
|
||||
|
||||
func (*ParenExpr) exprNode() {}
|
||||
|
||||
// ------------------------------------------------
|
||||
|
||||
// Parent::X
|
||||
type ScopingExpr struct {
|
||||
Parent Expr
|
||||
X Expr
|
||||
}
|
||||
|
||||
func (*ScopingExpr) exprNode() {}
|
||||
|
||||
// ------------------------------------------------
|
||||
|
||||
// X*
|
||||
type PointerType struct {
|
||||
X Expr
|
||||
}
|
||||
|
||||
func (*PointerType) exprNode() {}
|
||||
|
||||
// ------------------------------------------------
|
||||
|
||||
// X&
|
||||
type ReferenceType struct {
|
||||
X Expr
|
||||
}
|
||||
|
||||
func (*ReferenceType) exprNode() {}
|
||||
|
||||
// ------------------------------------------------
|
||||
|
||||
// Elt[Len]
|
||||
// Elt[]
|
||||
type ArrayType struct {
|
||||
Elt Expr
|
||||
Len Expr // optional
|
||||
}
|
||||
|
||||
func (*ArrayType) exprNode() {}
|
||||
|
||||
// ------------------------------------------------
|
||||
|
||||
type Comment struct {
|
||||
Text string // comment text (excluding '\n' for //-style comments)
|
||||
}
|
||||
|
||||
func (*Comment) exprNode() {}
|
||||
|
||||
type CommentGroup struct {
|
||||
List []*Comment // len(List) > 0
|
||||
}
|
||||
|
||||
func (*CommentGroup) exprNode() {}
|
||||
|
||||
// ------------------------------------------------
|
||||
|
||||
type Field struct {
|
||||
Doc *CommentGroup // associated documentation; or nil
|
||||
Type Expr // field/method/parameter type; or nil
|
||||
Names []*Ident // field/method/(type) parameter names; or nil
|
||||
Comment *CommentGroup // line comments; or nil
|
||||
}
|
||||
|
||||
func (*Field) exprNode() {}
|
||||
|
||||
type FieldList struct {
|
||||
List []*Field // field list; or nil
|
||||
}
|
||||
|
||||
func (*FieldList) exprNode() {}
|
||||
|
||||
// ------------------------------------------------
|
||||
|
||||
// Ret (*)(Params)
|
||||
type FuncType struct {
|
||||
Params *FieldList
|
||||
Ret Expr
|
||||
}
|
||||
|
||||
func (*FuncType) exprNode() {}
|
||||
|
||||
// ------------------------------------------------
|
||||
|
||||
// Template<Arg1, Arg2, ...>
|
||||
type InstantiationType struct {
|
||||
Template Expr
|
||||
Args *FieldList
|
||||
}
|
||||
|
||||
func (*InstantiationType) exprNode() {}
|
||||
|
||||
// =============================================================================
|
||||
// Declarations
|
||||
|
||||
type Location struct {
|
||||
File string
|
||||
}
|
||||
|
||||
type DeclBase struct {
|
||||
Doc *CommentGroup // associated documentation; or nil
|
||||
Loc *Location
|
||||
Parent Expr // namespace or class
|
||||
}
|
||||
|
||||
// ------------------------------------------------
|
||||
|
||||
// typedef Type Name;
|
||||
type TypedefDecl struct {
|
||||
DeclBase
|
||||
Type Expr
|
||||
Name *Ident
|
||||
}
|
||||
|
||||
func (*TypedefDecl) declNode() {}
|
||||
|
||||
// ------------------------------------------------
|
||||
|
||||
type EnumItem struct {
|
||||
Name *Ident
|
||||
Value Expr // optional
|
||||
}
|
||||
|
||||
// enum Name { Item1, Item2, ... };
|
||||
type EnumTypeDecl struct {
|
||||
DeclBase
|
||||
Name *Ident
|
||||
Items []*EnumItem
|
||||
}
|
||||
|
||||
func (*EnumTypeDecl) declNode() {}
|
||||
|
||||
// ------------------------------------------------
|
||||
|
||||
// Ret Name(Params);
|
||||
type FuncDecl struct {
|
||||
DeclBase
|
||||
Name *Ident
|
||||
Type *FuncType
|
||||
}
|
||||
|
||||
func (*FuncDecl) declNode() {}
|
||||
|
||||
// ------------------------------------------------
|
||||
|
||||
// struct/union/class Name { Field1, Field2, ... };
|
||||
type TypeDecl struct {
|
||||
DeclBase
|
||||
Tag Tag
|
||||
Fields *FieldList
|
||||
Methods []*FuncDecl
|
||||
}
|
||||
|
||||
func (*TypeDecl) declNode() {}
|
||||
|
||||
// =============================================================================
|
||||
// AST File
|
||||
|
||||
type File struct {
|
||||
Decls []Decl
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
27
cl/_testdata/async/in.go
Normal file
27
cl/_testdata/async/in.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package async
|
||||
|
||||
import (
|
||||
"github.com/goplus/llgo/x/async"
|
||||
)
|
||||
|
||||
func GenInts() (co *async.Promise[int]) {
|
||||
co.Yield(1)
|
||||
co.Yield(2)
|
||||
co.Yield(3)
|
||||
return
|
||||
}
|
||||
|
||||
func WrapGenInts() *async.Promise[int] {
|
||||
return GenInts()
|
||||
}
|
||||
|
||||
func UseGenInts() int {
|
||||
co := WrapGenInts()
|
||||
r := 0
|
||||
for !co.Done() {
|
||||
v := co.Value()
|
||||
r += v
|
||||
co.Next()
|
||||
}
|
||||
return r
|
||||
}
|
||||
201
cl/_testdata/async/out.ll
Normal file
201
cl/_testdata/async/out.ll
Normal file
@@ -0,0 +1,201 @@
|
||||
; ModuleID = 'async'
|
||||
source_filename = "async"
|
||||
|
||||
%"github.com/goplus/llgo/x/async.Promise[int]" = type { ptr, i64 }
|
||||
|
||||
@"async.init$guard" = global i1 false, align 1
|
||||
|
||||
define ptr @async.GenInts() presplitcoroutine {
|
||||
entry:
|
||||
%id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
|
||||
%frame.size = call i64 @llvm.coro.size.i64()
|
||||
%alloc.size = add i64 16, %frame.size
|
||||
%promise = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 %alloc.size)
|
||||
%need.dyn.alloc = call i1 @llvm.coro.alloc(token %id)
|
||||
br i1 %need.dyn.alloc, label %alloc, label %_llgo_5
|
||||
|
||||
alloc: ; preds = %entry
|
||||
%0 = getelementptr ptr, ptr %promise, i64 16
|
||||
br label %_llgo_5
|
||||
|
||||
clean: ; preds = %_llgo_8, %_llgo_7, %_llgo_6, %_llgo_5
|
||||
%1 = call ptr @llvm.coro.free(token %id, ptr %hdl)
|
||||
br label %suspend
|
||||
|
||||
suspend: ; preds = %_llgo_8, %_llgo_7, %_llgo_6, %_llgo_5, %clean
|
||||
%2 = call i1 @llvm.coro.end(ptr %hdl, i1 false, token none)
|
||||
ret ptr %promise
|
||||
|
||||
trap: ; preds = %_llgo_8
|
||||
call void @llvm.trap()
|
||||
unreachable
|
||||
|
||||
_llgo_5: ; preds = %alloc, %entry
|
||||
%frame = phi ptr [ null, %entry ], [ %0, %alloc ]
|
||||
%hdl = call ptr @llvm.coro.begin(token %id, ptr %frame)
|
||||
store ptr %hdl, ptr %promise, align 8
|
||||
call void @"github.com/goplus/llgo/x/async.(*Promise).setValue[int]"(ptr %promise, i64 1)
|
||||
%3 = call i8 @llvm.coro.suspend(token %id, i1 false)
|
||||
switch i8 %3, label %suspend [
|
||||
i8 0, label %_llgo_6
|
||||
i8 1, label %clean
|
||||
]
|
||||
|
||||
_llgo_6: ; preds = %_llgo_5
|
||||
call void @"github.com/goplus/llgo/x/async.(*Promise).setValue[int]"(ptr %promise, i64 2)
|
||||
%4 = call i8 @llvm.coro.suspend(token %id, i1 false)
|
||||
switch i8 %4, label %suspend [
|
||||
i8 0, label %_llgo_7
|
||||
i8 1, label %clean
|
||||
]
|
||||
|
||||
_llgo_7: ; preds = %_llgo_6
|
||||
call void @"github.com/goplus/llgo/x/async.(*Promise).setValue[int]"(ptr %promise, i64 3)
|
||||
%5 = call i8 @llvm.coro.suspend(token %id, i1 false)
|
||||
switch i8 %5, label %suspend [
|
||||
i8 0, label %_llgo_8
|
||||
i8 1, label %clean
|
||||
]
|
||||
|
||||
_llgo_8: ; preds = %_llgo_7
|
||||
%6 = call i8 @llvm.coro.suspend(token %id, i1 true)
|
||||
switch i8 %6, label %suspend [
|
||||
i8 0, label %trap
|
||||
i8 1, label %clean
|
||||
]
|
||||
}
|
||||
|
||||
define i64 @async.UseGenInts() {
|
||||
_llgo_0:
|
||||
%0 = call ptr @async.WrapGenInts()
|
||||
br label %_llgo_3
|
||||
|
||||
_llgo_1: ; preds = %_llgo_3
|
||||
%1 = call i64 @"github.com/goplus/llgo/x/async.(*Promise).Value[int]"(ptr %0)
|
||||
%2 = add i64 %3, %1
|
||||
call void @"github.com/goplus/llgo/x/async.(*Promise).Next[int]"(ptr %0)
|
||||
br label %_llgo_3
|
||||
|
||||
_llgo_2: ; preds = %_llgo_3
|
||||
ret i64 %3
|
||||
|
||||
_llgo_3: ; preds = %_llgo_1, %_llgo_0
|
||||
%3 = phi i64 [ 0, %_llgo_0 ], [ %2, %_llgo_1 ]
|
||||
%4 = call i1 @"github.com/goplus/llgo/x/async.(*Promise).Done[int]"(ptr %0)
|
||||
br i1 %4, label %_llgo_2, label %_llgo_1
|
||||
}
|
||||
|
||||
define ptr @async.WrapGenInts() presplitcoroutine {
|
||||
entry:
|
||||
%id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
|
||||
%frame.size = call i64 @llvm.coro.size.i64()
|
||||
%alloc.size = add i64 16, %frame.size
|
||||
%promise = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 %alloc.size)
|
||||
%need.dyn.alloc = call i1 @llvm.coro.alloc(token %id)
|
||||
br i1 %need.dyn.alloc, label %alloc, label %_llgo_5
|
||||
|
||||
alloc: ; preds = %entry
|
||||
%0 = getelementptr ptr, ptr %promise, i64 16
|
||||
br label %_llgo_5
|
||||
|
||||
clean: ; preds = %_llgo_5
|
||||
%1 = call ptr @llvm.coro.free(token %id, ptr %hdl)
|
||||
br label %suspend
|
||||
|
||||
suspend: ; preds = %_llgo_5, %clean
|
||||
%2 = call i1 @llvm.coro.end(ptr %hdl, i1 false, token none)
|
||||
ret ptr %promise
|
||||
|
||||
trap: ; preds = %_llgo_5
|
||||
call void @llvm.trap()
|
||||
unreachable
|
||||
|
||||
_llgo_5: ; preds = %alloc, %entry
|
||||
%frame = phi ptr [ null, %entry ], [ %0, %alloc ]
|
||||
%hdl = call ptr @llvm.coro.begin(token %id, ptr %frame)
|
||||
store ptr %hdl, ptr %promise, align 8
|
||||
%3 = call ptr @async.GenInts()
|
||||
%4 = call i8 @llvm.coro.suspend(token %id, i1 true)
|
||||
switch i8 %4, label %suspend [
|
||||
i8 0, label %trap
|
||||
i8 1, label %clean
|
||||
]
|
||||
}
|
||||
|
||||
define void @async.init() {
|
||||
_llgo_0:
|
||||
%0 = load i1, ptr @"async.init$guard", align 1
|
||||
br i1 %0, label %_llgo_2, label %_llgo_1
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
store i1 true, ptr @"async.init$guard", align 1
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: read)
|
||||
declare token @llvm.coro.id(i32, ptr readnone, ptr nocapture readonly, ptr)
|
||||
|
||||
; Function Attrs: nounwind memory(none)
|
||||
declare i64 @llvm.coro.size.i64()
|
||||
|
||||
declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64)
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i1 @llvm.coro.alloc(token)
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @llvm.coro.begin(token, ptr writeonly)
|
||||
|
||||
; Function Attrs: nounwind memory(argmem: read)
|
||||
declare ptr @llvm.coro.free(token, ptr nocapture readonly)
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i1 @llvm.coro.end(ptr, i1, token)
|
||||
|
||||
; Function Attrs: cold noreturn nounwind memory(inaccessiblemem: write)
|
||||
declare void @llvm.trap()
|
||||
|
||||
define void @"github.com/goplus/llgo/x/async.(*Promise).setValue[int]"(ptr %0, i64 %1) {
|
||||
_llgo_0:
|
||||
%2 = getelementptr inbounds %"github.com/goplus/llgo/x/async.Promise[int]", ptr %0, i32 0, i32 1
|
||||
store i64 %1, ptr %2, align 4
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i8 @llvm.coro.suspend(token, i1)
|
||||
|
||||
define i1 @"github.com/goplus/llgo/x/async.(*Promise).Done[int]"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/x/async.Promise[int]", ptr %0, i32 0, i32 0
|
||||
%2 = load ptr, ptr %1, align 8
|
||||
%3 = call i1 @llvm.coro.done(ptr %2)
|
||||
%4 = zext i1 %3 to i64
|
||||
%5 = trunc i64 %4 to i8
|
||||
%6 = icmp ne i8 %5, 0
|
||||
ret i1 %6
|
||||
}
|
||||
|
||||
define i64 @"github.com/goplus/llgo/x/async.(*Promise).Value[int]"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/x/async.Promise[int]", ptr %0, i32 0, i32 1
|
||||
%2 = load i64, ptr %1, align 4
|
||||
ret i64 %2
|
||||
}
|
||||
|
||||
define void @"github.com/goplus/llgo/x/async.(*Promise).Next[int]"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/x/async.Promise[int]", ptr %0, i32 0, i32 0
|
||||
%2 = load ptr, ptr %1, align 8
|
||||
call void @llvm.coro.resume(ptr %2)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind memory(argmem: readwrite)
|
||||
declare i1 @llvm.coro.done(ptr nocapture readonly)
|
||||
|
||||
declare void @llvm.coro.resume(ptr)
|
||||
|
||||
119
cl/async.go
Normal file
119
cl/async.go
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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 cl
|
||||
|
||||
import (
|
||||
"go/constant"
|
||||
"go/types"
|
||||
"strings"
|
||||
|
||||
llssa "github.com/goplus/llgo/ssa"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
)
|
||||
|
||||
// TODO(lijie): need more generics, shouldn't limit to async.Promise
|
||||
func promiseType(ty types.Type) (types.Type, bool) {
|
||||
// ty is a generic type, so we need to check the package path and type name
|
||||
if ptrTy, ok := ty.(*types.Pointer); ok {
|
||||
ty = ptrTy.Elem()
|
||||
if ty, ok := ty.(*types.Named); ok {
|
||||
if ty.Obj().Pkg() == nil {
|
||||
return nil, false
|
||||
}
|
||||
if ty.Obj().Pkg().Path() == "github.com/goplus/llgo/x/async" && ty.Obj().Name() == "Promise" {
|
||||
return ty, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// check function return async.Promise[T]
|
||||
// TODO(lijie): make it generic
|
||||
func isAsyncFunc(sig *types.Signature) bool {
|
||||
r := sig.Results()
|
||||
if r.Len() != 1 {
|
||||
return false
|
||||
}
|
||||
ty := r.At(0).Type()
|
||||
_, ok := promiseType(ty)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (p *context) coAwait(b llssa.Builder, args []ssa.Value) llssa.Expr {
|
||||
if !isAsyncFunc(b.Func.RawType().(*types.Signature)) {
|
||||
panic("coAwait(promise *T) T: invalid context")
|
||||
}
|
||||
if len(args) == 1 {
|
||||
// promise := p.compileValue(b, args[0])
|
||||
b.Unreachable()
|
||||
// return b.CoroutineAwait(promise)
|
||||
}
|
||||
panic("coAwait(promise *T) T: invalid arguments")
|
||||
}
|
||||
|
||||
func (p *context) coSuspend(b llssa.Builder, final llssa.Expr) {
|
||||
b.CoSuspend(b.AsyncToken(), final, nil)
|
||||
}
|
||||
|
||||
func (p *context) coDone(b llssa.Builder, args []ssa.Value) llssa.Expr {
|
||||
if len(args) != 1 {
|
||||
panic("coDone(promise *T): invalid arguments")
|
||||
}
|
||||
hdl := p.compileValue(b, args[0])
|
||||
return b.CoDone(hdl)
|
||||
}
|
||||
|
||||
func (p *context) coResume(b llssa.Builder, args []ssa.Value) {
|
||||
if len(args) == 1 {
|
||||
hdl := p.compileValue(b, args[0])
|
||||
b.CoResume(hdl)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *context) getSetValueFunc(fn *ssa.Function) llssa.Function {
|
||||
typ := fn.Signature.Recv().Type()
|
||||
mthds := p.goProg.MethodSets.MethodSet(typ)
|
||||
for i := 0; i < mthds.Len(); i++ {
|
||||
m := mthds.At(i)
|
||||
if ssaMthd := p.goProg.MethodValue(m); ssaMthd != nil {
|
||||
if ssaMthd.Name() == "setValue" || strings.HasPrefix(ssaMthd.Name(), "setValue[") {
|
||||
setValueFn, _, _ := p.compileFunction(ssaMthd)
|
||||
return setValueFn
|
||||
}
|
||||
}
|
||||
}
|
||||
panic("method setValue not found on type " + typ.String())
|
||||
}
|
||||
|
||||
func (p *context) coReturn(b llssa.Builder, fn *ssa.Function, args []ssa.Value) {
|
||||
setValueFn := p.getSetValueFunc(fn)
|
||||
value := p.compileValue(b, args[1])
|
||||
b.CoReturn(setValueFn, value)
|
||||
}
|
||||
|
||||
func (p *context) coYield(b llssa.Builder, fn *ssa.Function, args []ssa.Value) {
|
||||
setValueFn := p.getSetValueFunc(fn)
|
||||
value := p.compileValue(b, args[1])
|
||||
// TODO(lijie): find whether the co.Yield/co.Return is the last instruction
|
||||
final := b.Const(constant.MakeBool(false), b.Prog.Bool())
|
||||
b.CoYield(setValueFn, value, final)
|
||||
}
|
||||
|
||||
func (p *context) coRun(b llssa.Builder, args []ssa.Value) {
|
||||
panic("coRun(): not implemented")
|
||||
}
|
||||
@@ -282,3 +282,68 @@ func TestErrVarOf(t *testing.T) {
|
||||
g := &ssa.Global{Pkg: ssaPkg}
|
||||
ctx.varOf(nil, g)
|
||||
}
|
||||
|
||||
func TestContextResolveLinkname(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
context *context
|
||||
input string
|
||||
want string
|
||||
panics bool
|
||||
}{
|
||||
{
|
||||
name: "Normal",
|
||||
context: &context{
|
||||
link: map[string]string{
|
||||
"foo": "C.bar",
|
||||
},
|
||||
},
|
||||
input: "foo",
|
||||
want: "bar",
|
||||
},
|
||||
{
|
||||
name: "MultipleLinks",
|
||||
context: &context{
|
||||
link: map[string]string{
|
||||
"foo1": "C.bar1",
|
||||
"foo2": "C.bar2",
|
||||
},
|
||||
},
|
||||
input: "foo2",
|
||||
want: "bar2",
|
||||
},
|
||||
{
|
||||
name: "NoLink",
|
||||
context: &context{link: map[string]string{}},
|
||||
input: "foo",
|
||||
want: "foo",
|
||||
},
|
||||
{
|
||||
name: "InvalidLink",
|
||||
context: &context{
|
||||
link: map[string]string{
|
||||
"foo": "invalid.bar",
|
||||
},
|
||||
},
|
||||
input: "foo",
|
||||
panics: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.panics {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Error("want panic")
|
||||
}
|
||||
}()
|
||||
}
|
||||
got := tt.context.resolveLinkname(tt.input)
|
||||
if !tt.panics {
|
||||
if got != tt.want {
|
||||
t.Errorf("got %q, want %q", got, tt.want)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/goplus/llgo/cl/blocks"
|
||||
"github.com/goplus/llgo/internal/typepatch"
|
||||
@@ -217,6 +218,7 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun
|
||||
log.Println("==> NewFunc", name, "type:", sig.Recv(), sig, "ftype:", ftype)
|
||||
}
|
||||
}
|
||||
async := isAsyncFunc(f.Signature)
|
||||
if fn == nil {
|
||||
if name == "main" {
|
||||
argc := types.NewParam(token.NoPos, pkgTypes, "", types.Typ[types.Int32])
|
||||
@@ -226,13 +228,24 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun
|
||||
results := types.NewTuple(ret)
|
||||
sig = types.NewSignatureType(nil, nil, nil, params, results, false)
|
||||
}
|
||||
fn = pkg.NewFuncEx(name, sig, llssa.Background(ftype), hasCtx)
|
||||
fn = pkg.NewFuncEx(name, sig, llssa.Background(ftype), hasCtx, async)
|
||||
}
|
||||
|
||||
nBlkOff := 0
|
||||
if nblk := len(f.Blocks); nblk > 0 {
|
||||
var entryBlk, allocBlk, cleanBlk, suspdBlk, trapBlk, beginBlk llssa.BasicBlock
|
||||
if async {
|
||||
nBlkOff = 5
|
||||
entryBlk = fn.MakeBlock("entry")
|
||||
allocBlk = fn.MakeBlock("alloc")
|
||||
cleanBlk = fn.MakeBlock("clean")
|
||||
suspdBlk = fn.MakeBlock("suspend")
|
||||
trapBlk = fn.MakeBlock("trap")
|
||||
}
|
||||
fn.MakeBlocks(nblk) // to set fn.HasBody() = true
|
||||
beginBlk = fn.Block(nBlkOff)
|
||||
if f.Recover != nil { // set recover block
|
||||
fn.SetRecover(fn.Block(f.Recover.Index))
|
||||
// TODO(lijie): fix this for async function because of the block offset increase
|
||||
fn.SetRecover(fn.Block(f.Recover.Index + nBlkOff))
|
||||
}
|
||||
p.inits = append(p.inits, func() {
|
||||
p.fn = fn
|
||||
@@ -248,6 +261,10 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun
|
||||
log.Println("==> FuncBody", name)
|
||||
}
|
||||
b := fn.NewBuilder()
|
||||
b.SetBlockOffset(nBlkOff)
|
||||
if async {
|
||||
b.BeginAsync(fn, entryBlk, allocBlk, cleanBlk, suspdBlk, trapBlk, beginBlk)
|
||||
}
|
||||
p.bvals = make(map[ssa.Value]llssa.Expr)
|
||||
off := make([]int, len(f.Blocks))
|
||||
for i, block := range f.Blocks {
|
||||
@@ -283,7 +300,7 @@ func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, n int, do
|
||||
var pkg = p.pkg
|
||||
var fn = p.fn
|
||||
var instrs = block.Instrs[n:]
|
||||
var ret = fn.Block(block.Index)
|
||||
var ret = fn.Block(block.Index + b.BlockOffset())
|
||||
b.SetBlock(ret)
|
||||
if doModInit {
|
||||
if pyModInit = p.pyMod != ""; pyModInit {
|
||||
@@ -321,7 +338,7 @@ func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, n int, do
|
||||
modPtr := pkg.PyNewModVar(modName, true).Expr
|
||||
mod := b.Load(modPtr)
|
||||
cond := b.BinOp(token.NEQ, mod, prog.Nil(mod.Type))
|
||||
newBlk := fn.MakeBlock()
|
||||
newBlk := fn.MakeBlock("")
|
||||
b.If(cond, jumpTo, newBlk)
|
||||
b.SetBlockEx(newBlk, llssa.AtEnd, false)
|
||||
b.Store(modPtr, b.PyImportMod(modPath))
|
||||
@@ -653,7 +670,11 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) {
|
||||
results = make([]llssa.Expr, 1)
|
||||
results[0] = p.prog.IntVal(0, p.prog.CInt())
|
||||
}
|
||||
if b.Async() {
|
||||
b.EndAsync()
|
||||
} else {
|
||||
b.Return(results...)
|
||||
}
|
||||
case *ssa.If:
|
||||
fn := p.fn
|
||||
cond := p.compileValue(b, v.Cond)
|
||||
@@ -812,6 +833,7 @@ func NewPackageEx(prog llssa.Program, patches Patches, pkg *ssa.Package, files [
|
||||
ctx.initPyModule()
|
||||
ctx.initFiles(pkgPath, files)
|
||||
ret.SetPatch(ctx.patchType)
|
||||
ret.SetResolveLinkname(ctx.resolveLinkname)
|
||||
|
||||
if hasPatch {
|
||||
skips := ctx.skips
|
||||
@@ -904,4 +926,15 @@ func (p *context) patchType(typ types.Type) types.Type {
|
||||
return typ
|
||||
}
|
||||
|
||||
func (p *context) resolveLinkname(name string) string {
|
||||
if link, ok := p.link[name]; ok {
|
||||
prefix, ltarget, _ := strings.Cut(link, ".")
|
||||
if prefix != "C" {
|
||||
panic("resolveLinkname: invalid link: " + link)
|
||||
}
|
||||
return ltarget
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@@ -53,7 +53,7 @@ func TestFromTestrt(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFromTestdata(t *testing.T) {
|
||||
cltest.FromDir(t, "", "./_testdata", false)
|
||||
cltest.FromDir(t, "", "./_testdata", true)
|
||||
}
|
||||
|
||||
func TestFromTestpymath(t *testing.T) {
|
||||
@@ -124,3 +124,114 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
||||
func TestAsyncFunc(t *testing.T) {
|
||||
testCompile(t, `package foo
|
||||
|
||||
import "github.com/goplus/llgo/x/async"
|
||||
|
||||
func GenInts() (co *async.Promise[int]) {
|
||||
co.Yield(1)
|
||||
co.Yield(2)
|
||||
return
|
||||
}
|
||||
`, `; ModuleID = 'foo'
|
||||
source_filename = "foo"
|
||||
|
||||
@"foo.init$guard" = global i1 false, align 1
|
||||
|
||||
define ptr @foo.GenInts() presplitcoroutine {
|
||||
entry:
|
||||
%id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
|
||||
%frame.size = call i64 @llvm.coro.size.i64()
|
||||
%alloc.size = add i64 16, %frame.size
|
||||
%promise = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 %alloc.size)
|
||||
%need.dyn.alloc = call i1 @llvm.coro.alloc(token %id)
|
||||
br i1 %need.dyn.alloc, label %alloc, label %_llgo_5
|
||||
|
||||
alloc: ; preds = %entry
|
||||
%0 = getelementptr ptr, ptr %promise, i64 16
|
||||
br label %_llgo_5
|
||||
|
||||
clean: ; preds = %_llgo_7, %_llgo_6, %_llgo_5
|
||||
%1 = call ptr @llvm.coro.free(token %id, ptr %hdl)
|
||||
br label %suspend
|
||||
|
||||
suspend: ; preds = %_llgo_7, %_llgo_6, %_llgo_5, %clean
|
||||
%2 = call i1 @llvm.coro.end(ptr %hdl, i1 false, token none)
|
||||
ret ptr %promise
|
||||
|
||||
trap: ; preds = %_llgo_7
|
||||
call void @llvm.trap()
|
||||
unreachable
|
||||
|
||||
_llgo_5: ; preds = %alloc, %entry
|
||||
%frame = phi ptr [ null, %entry ], [ %0, %alloc ]
|
||||
%hdl = call ptr @llvm.coro.begin(token %id, ptr %frame)
|
||||
store ptr %hdl, ptr %promise, align 8
|
||||
call void @"github.com/goplus/llgo/x/async.(*Promise).setValue[int]"(ptr %promise, i64 1)
|
||||
%3 = call i8 @llvm.coro.suspend(token %id, i1 false)
|
||||
switch i8 %3, label %suspend [
|
||||
i8 0, label %_llgo_6
|
||||
i8 1, label %clean
|
||||
]
|
||||
|
||||
_llgo_6: ; preds = %_llgo_5
|
||||
call void @"github.com/goplus/llgo/x/async.(*Promise).setValue[int]"(ptr %promise, i64 2)
|
||||
%4 = call i8 @llvm.coro.suspend(token %id, i1 false)
|
||||
switch i8 %4, label %suspend [
|
||||
i8 0, label %_llgo_7
|
||||
i8 1, label %clean
|
||||
]
|
||||
|
||||
_llgo_7: ; preds = %_llgo_6
|
||||
%5 = call i8 @llvm.coro.suspend(token %id, i1 true)
|
||||
switch i8 %5, label %suspend [
|
||||
i8 0, label %trap
|
||||
i8 1, label %clean
|
||||
]
|
||||
}
|
||||
|
||||
define void @foo.init() {
|
||||
_llgo_0:
|
||||
%0 = load i1, ptr @"foo.init$guard", align 1
|
||||
br i1 %0, label %_llgo_2, label %_llgo_1
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
store i1 true, ptr @"foo.init$guard", align 1
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: read)
|
||||
declare token @llvm.coro.id(i32, ptr readnone, ptr nocapture readonly, ptr)
|
||||
|
||||
; Function Attrs: nounwind memory(none)
|
||||
declare i64 @llvm.coro.size.i64()
|
||||
|
||||
declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64)
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i1 @llvm.coro.alloc(token)
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @llvm.coro.begin(token, ptr writeonly)
|
||||
|
||||
; Function Attrs: nounwind memory(argmem: read)
|
||||
declare ptr @llvm.coro.free(token, ptr nocapture readonly)
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i1 @llvm.coro.end(ptr, i1, token)
|
||||
|
||||
; Function Attrs: cold noreturn nounwind memory(inaccessiblemem: write)
|
||||
declare void @llvm.trap()
|
||||
|
||||
declare void @"github.com/goplus/llgo/x/async.(*Promise).setValue[int]"(ptr, i64)
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i8 @llvm.coro.suspend(token, i1)
|
||||
|
||||
`)
|
||||
}
|
||||
|
||||
11
cl/import.go
11
cl/import.go
@@ -412,7 +412,16 @@ const (
|
||||
llgoAtomicUMax = llgoAtomicOpBase + llssa.OpUMax
|
||||
llgoAtomicUMin = llgoAtomicOpBase + llssa.OpUMin
|
||||
|
||||
llgoAtomicOpLast = llgoAtomicOpBase + int(llssa.OpUMin)
|
||||
llgoCoBase = llgoInstrBase + 0x30
|
||||
llgoCoAwait = llgoCoBase + 0
|
||||
llgoCoSuspend = llgoCoBase + 1
|
||||
llgoCoDone = llgoCoBase + 2
|
||||
llgoCoResume = llgoCoBase + 3
|
||||
llgoCoReturn = llgoCoBase + 4
|
||||
llgoCoYield = llgoCoBase + 5
|
||||
llgoCoRun = llgoCoBase + 6
|
||||
|
||||
llgoAtomicOpLast = llgoCoRun
|
||||
)
|
||||
|
||||
func (p *context) funcName(fn *ssa.Function, ignore bool) (*types.Package, string, int) {
|
||||
|
||||
25
cl/instr.go
25
cl/instr.go
@@ -237,6 +237,14 @@ var llgoInstrs = map[string]int{
|
||||
"atomicMin": int(llgoAtomicMin),
|
||||
"atomicUMax": int(llgoAtomicUMax),
|
||||
"atomicUMin": int(llgoAtomicUMin),
|
||||
|
||||
"coAwait": int(llgoCoAwait),
|
||||
"coResume": int(llgoCoResume),
|
||||
"coSuspend": int(llgoCoSuspend),
|
||||
"coDone": int(llgoCoDone),
|
||||
"coReturn": int(llgoCoReturn),
|
||||
"coYield": int(llgoCoYield),
|
||||
"coRun": int(llgoCoRun),
|
||||
}
|
||||
|
||||
// funcOf returns a function by name and set ftype = goFunc, cFunc, etc.
|
||||
@@ -265,7 +273,8 @@ func (p *context) funcOf(fn *ssa.Function) (aFn llssa.Function, pyFn llssa.PyObj
|
||||
return nil, nil, ignoredFunc
|
||||
}
|
||||
sig := fn.Signature
|
||||
aFn = pkg.NewFuncEx(name, sig, llssa.Background(ftype), false)
|
||||
async := isAsyncFunc(sig)
|
||||
aFn = pkg.NewFuncEx(name, sig, llssa.Background(ftype), false, async)
|
||||
}
|
||||
}
|
||||
return
|
||||
@@ -390,6 +399,20 @@ func (p *context) call(b llssa.Builder, act llssa.DoAction, call *ssa.CallCommon
|
||||
ret = p.funcAddr(b, args)
|
||||
case llgoUnreachable: // func unreachable()
|
||||
b.Unreachable()
|
||||
case llgoCoAwait:
|
||||
ret = p.coAwait(b, args)
|
||||
case llgoCoSuspend:
|
||||
p.coSuspend(b, p.prog.BoolVal(false))
|
||||
case llgoCoDone:
|
||||
return p.coDone(b, args)
|
||||
case llgoCoResume:
|
||||
p.coResume(b, args)
|
||||
case llgoCoReturn:
|
||||
p.coReturn(b, cv, args)
|
||||
case llgoCoYield:
|
||||
p.coYield(b, cv, args)
|
||||
case llgoCoRun:
|
||||
p.coRun(b, args)
|
||||
default:
|
||||
if ftype >= llgoAtomicOpBase && ftype <= llgoAtomicOpLast {
|
||||
ret = p.atomic(b, llssa.AtomicOp(ftype-llgoAtomicOpBase), args)
|
||||
|
||||
@@ -31,7 +31,7 @@ type StringView = string
|
||||
|
||||
// String represents a C++ std::string object.
|
||||
type String struct {
|
||||
Unused [3]uintptr
|
||||
Unused [3 * unsafe.Sizeof(0)]byte
|
||||
}
|
||||
|
||||
// llgo:link (*String).InitEmpty C.stdStringInitEmpty
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
How to support a C/C++ Library
|
||||
=====
|
||||
## Symbol Visibility
|
||||
When llgo needs to link C or C++ libraries, symbol visibility is a crucial concept. It determines how C/C++ functions and methods are linked and utilized within llgo. Symbol visibility significantly impacts the handling of symbols in llgo bindings: visible symbols can typically be directly linked, while invisible symbols require wrapper functions.
|
||||
|
||||
The accessibility of symbols, particularly destructors, in dynamic libraries depends on their definition method. For instance, destructors that are explicitly declared in header files and implemented as non-inline functions in .cpp files typically appear in the dynamic library's symbol table, making them visible and directly linkable.
|
||||
|
||||
**Visible Symbols:** These are symbols that can be found in the dynamic library. They typically represent public API functions and methods.
|
||||
|
||||
* Example: A class constructor explicitly declared in the header and implemented in a .cpp file.
|
||||
|
||||
* Example: A non-inline destructor declared in the header and implemented in a .cpp file.
|
||||
|
||||
**Invisible Symbols:** These are symbols that cannot be found in the dynamic library. This may include inline functions, templates, or certain constructors and destructors.
|
||||
|
||||
* Example: A default constructor not explicitly declared.
|
||||
|
||||
* Example: An inline destructor or a compiler-generated default destructor.
|
||||
|
||||
## Support a C Library
|
||||
|
||||
@@ -27,7 +43,7 @@ int ini_parse(const char* filename, ini_handler handler, void* user);
|
||||
|
||||
3. Create the corresponding Go file
|
||||
|
||||
```c
|
||||
```bash
|
||||
inih/
|
||||
├── _demo
|
||||
├── inih_demo
|
||||
@@ -296,46 +312,6 @@ func ParseError() c.Int
|
||||
Unused [32]byte
|
||||
}
|
||||
```
|
||||
- Constructor
|
||||
|
||||
- Constructor is explicitly declared in the class (can find the corresponding symbol in the dynamic library):
|
||||
|
||||
Bind to the `InitFromBuffer` method of the struct and call it in the `NewReaderFile` function to initialize the class and return the class for Go to use.
|
||||
|
||||
```go
|
||||
// NewReaderFile creates a new INIReader instance.
|
||||
func NewReaderFile(fileName *std.String) (ret Reader) {
|
||||
ret.InitFromFile(fileName)
|
||||
return
|
||||
}
|
||||
/*
|
||||
class INIReader
|
||||
{
|
||||
public:
|
||||
explicit INIReader(const char *buffer, size_t buffer_size);
|
||||
}
|
||||
*/
|
||||
|
||||
// llgo:link (*Reader).InitFromBuffer C._ZN9INIReaderC1EPKcm
|
||||
func (r *Reader) InitFromBuffer(buffer *c.Char, bufferSize uintptr) {}
|
||||
```
|
||||
- Constructor is not explicitly declared in the class (cannot find the corresponding symbol in the dynamic library)
|
||||
|
||||
If the destructor is not explicitly declared in the source code, the compiler will automatically generate a default destructor. Use `extern "C"` to wrap it in cppWrap.cpp:
|
||||
|
||||
```c
|
||||
extern "C" void INIReaderInit(INIReader* r)
|
||||
{
|
||||
r->INIReader();
|
||||
}
|
||||
```
|
||||
|
||||
Link in Go:
|
||||
|
||||
```go
|
||||
// llgo:link (*Reader).INIReaderInit C.INIReaderInit
|
||||
func (r *Reader) INIReaderInit() {}
|
||||
```
|
||||
- Class Methods
|
||||
|
||||
For general methods of the class, directly use `llgo:link` to link:
|
||||
@@ -346,22 +322,65 @@ func ParseError() c.Int
|
||||
return 0
|
||||
}
|
||||
```
|
||||
- Constructor
|
||||
|
||||
- Explicitly Constructor:
|
||||
|
||||
```cpp
|
||||
class INIReader {
|
||||
public:
|
||||
// Construct INIReader and parse given filename.
|
||||
INI_API explicit INIReader(const std::string &filename);
|
||||
}
|
||||
```
|
||||
Bind to the `InitFromFile` method of the struct and call it in the `NewReaderFile` function to initialize the class and return the class for Go to use.
|
||||
|
||||
The following long string starting with `_ZN9INI` is the corresponding function prototype in the symbol table for `INIReader(const std::string &filename)`
|
||||
```go
|
||||
// llgo:link (*Reader).InitFromFile C._ZN9INIReaderC1ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE
|
||||
func (r *Reader) InitFromFile(fileName *std.String) {}
|
||||
|
||||
// NewReaderFile creates a new INIReader instance.
|
||||
func NewReaderFile(fileName *std.String) (ret Reader) {
|
||||
ret.InitFromFile(fileName)
|
||||
return
|
||||
}
|
||||
```
|
||||
- Implicitly Constructor
|
||||
|
||||
In typical implementations of the inih library, directly invoking implicit constructors to instantiate reader objects is not recommended. For detailed examples of how bindings effectively handle non-exported symbols, please refer to the [Templates and Inlines](#templates-and-inlines) section.
|
||||
|
||||
Template or inline methods of the class will be introduced in the next section.
|
||||
- Destructor
|
||||
|
||||
Similar to the constructor process, after creating the class, use `defer` to call it explicitly:
|
||||
Explicitly declared and non-inline destructors can be directly linked, consistent with the linking method of general class methods (see "Class Methods" section). For destructors that do not appear in the dynamic library's symbol table, a wrapper layer implementation is required. This wrapper in the C++ wrapper file (e.g., cppWrap.cpp) looks like:
|
||||
|
||||
```cpp
|
||||
extern "C" {
|
||||
void INIReaderDispose(INIReader* r) {
|
||||
r->~INIReader();
|
||||
}
|
||||
} // extern "C"
|
||||
```
|
||||
This wrapper function explicitly calls the object's destructor. By using extern "C", we ensure that this function can be called by C code, allowing Go to link to it.
|
||||
In the Go file:
|
||||
```go
|
||||
// llgo:link (*Reader).Dispose C.INIReaderDispose
|
||||
func (r *Reader) Dispose() {}
|
||||
```
|
||||
Here we link the Go Dispose method to the C++ wrapped INIReaderDispose function.
|
||||
In actual usage:
|
||||
We use defer to ensure that the Dispose method is called when the reader object goes out of scope, thus properly releasing resources.
|
||||
```go
|
||||
reader := inih.NewReader(c.Str(buf), uintptr(len(buf)))
|
||||
defer reader.Dispose()
|
||||
```
|
||||
This situation is analogous to the handling of inline functions and templates described in the following section.
|
||||
|
||||
#### Templates and Inlines
|
||||
|
||||
Templates or inlines do not generate symbols in dynamic libraries (dylib) (default constructors and destructors). To ensure that you can use C style symbols to link template or inline functions, create a C++ file and wrap it with `extern "C"`, then bind the functions directly in Go.
|
||||
|
||||
```c
|
||||
```cpp
|
||||
// Using std::string as an example, not needed for migrating inih
|
||||
extern "C" void stdStringInitFromCStrLen(std::string* s, const char* cstr, size_t len) {
|
||||
new(s) std::string(cstr, len);
|
||||
|
||||
612
internal/build/_overlay/go/parser/resolver.go
Normal file
612
internal/build/_overlay/go/parser/resolver.go
Normal file
@@ -0,0 +1,612 @@
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package parser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const debugResolve = false
|
||||
|
||||
// resolveFile walks the given file to resolve identifiers within the file
|
||||
// scope, updating ast.Ident.Obj fields with declaration information.
|
||||
//
|
||||
// If declErr is non-nil, it is used to report declaration errors during
|
||||
// resolution. tok is used to format position in error messages.
|
||||
func resolveFile(file *ast.File, handle *token.File, declErr func(token.Pos, string)) {
|
||||
pkgScope := ast.NewScope(nil)
|
||||
r := &resolver{
|
||||
handle: handle,
|
||||
declErr: declErr,
|
||||
topScope: pkgScope,
|
||||
pkgScope: pkgScope,
|
||||
depth: 1,
|
||||
}
|
||||
|
||||
for _, decl := range file.Decls {
|
||||
ast.Walk(r, decl)
|
||||
}
|
||||
|
||||
r.closeScope()
|
||||
assert(r.topScope == nil, "unbalanced scopes")
|
||||
assert(r.labelScope == nil, "unbalanced label scopes")
|
||||
|
||||
// resolve global identifiers within the same file
|
||||
i := 0
|
||||
for _, ident := range r.unresolved {
|
||||
// i <= index for current ident
|
||||
assert(ident.Obj == unresolved, "object already resolved")
|
||||
ident.Obj = r.pkgScope.Lookup(ident.Name) // also removes unresolved sentinel
|
||||
if ident.Obj == nil {
|
||||
r.unresolved[i] = ident
|
||||
i++
|
||||
} else if debugResolve {
|
||||
pos := ident.Obj.Decl.(interface{ Pos() token.Pos }).Pos()
|
||||
r.trace("resolved %s@%v to package object %v", ident.Name, ident.Pos(), pos)
|
||||
}
|
||||
}
|
||||
file.Scope = r.pkgScope
|
||||
file.Unresolved = r.unresolved[0:i]
|
||||
}
|
||||
|
||||
const maxScopeDepth int = 1e3
|
||||
|
||||
type resolver struct {
|
||||
handle *token.File
|
||||
declErr func(token.Pos, string)
|
||||
|
||||
// Ordinary identifier scopes
|
||||
pkgScope *ast.Scope // pkgScope.Outer == nil
|
||||
topScope *ast.Scope // top-most scope; may be pkgScope
|
||||
unresolved []*ast.Ident // unresolved identifiers
|
||||
depth int // scope depth
|
||||
|
||||
// Label scopes
|
||||
// (maintained by open/close LabelScope)
|
||||
labelScope *ast.Scope // label scope for current function
|
||||
targetStack [][]*ast.Ident // stack of unresolved labels
|
||||
}
|
||||
|
||||
func (r *resolver) trace(format string, args ...any) {
|
||||
fmt.Println(strings.Repeat(". ", r.depth) + r.sprintf(format, args...))
|
||||
}
|
||||
|
||||
func (r *resolver) sprintf(format string, args ...any) string {
|
||||
for i, arg := range args {
|
||||
switch arg := arg.(type) {
|
||||
case token.Pos:
|
||||
args[i] = r.handle.Position(arg)
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf(format, args...)
|
||||
}
|
||||
|
||||
func (r *resolver) openScope(pos token.Pos) {
|
||||
r.depth++
|
||||
if r.depth > maxScopeDepth {
|
||||
panic(bailout{pos: pos, msg: "exceeded max scope depth during object resolution"})
|
||||
}
|
||||
if debugResolve {
|
||||
r.trace("opening scope @%v", pos)
|
||||
}
|
||||
r.topScope = ast.NewScope(r.topScope)
|
||||
}
|
||||
|
||||
func (r *resolver) closeScope() {
|
||||
r.depth--
|
||||
if debugResolve {
|
||||
r.trace("closing scope")
|
||||
}
|
||||
r.topScope = r.topScope.Outer
|
||||
}
|
||||
|
||||
func (r *resolver) openLabelScope() {
|
||||
r.labelScope = ast.NewScope(r.labelScope)
|
||||
r.targetStack = append(r.targetStack, nil)
|
||||
}
|
||||
|
||||
func (r *resolver) closeLabelScope() {
|
||||
// resolve labels
|
||||
n := len(r.targetStack) - 1
|
||||
scope := r.labelScope
|
||||
for _, ident := range r.targetStack[n] {
|
||||
ident.Obj = scope.Lookup(ident.Name)
|
||||
if ident.Obj == nil && r.declErr != nil {
|
||||
r.declErr(ident.Pos(), fmt.Sprintf("label %s undefined", ident.Name))
|
||||
}
|
||||
}
|
||||
// pop label scope
|
||||
r.targetStack = r.targetStack[0:n]
|
||||
r.labelScope = r.labelScope.Outer
|
||||
}
|
||||
|
||||
func (r *resolver) declare(decl, data any, scope *ast.Scope, kind ast.ObjKind, idents ...*ast.Ident) {
|
||||
for _, ident := range idents {
|
||||
if ident.Obj != nil {
|
||||
panic(fmt.Sprintf("%v: identifier %s already declared or resolved", ident.Pos(), ident.Name))
|
||||
}
|
||||
obj := ast.NewObj(kind, ident.Name)
|
||||
// remember the corresponding declaration for redeclaration
|
||||
// errors and global variable resolution/typechecking phase
|
||||
obj.Decl = decl
|
||||
obj.Data = data
|
||||
// Identifiers (for receiver type parameters) are written to the scope, but
|
||||
// never set as the resolved object. See issue #50956.
|
||||
if _, ok := decl.(*ast.Ident); !ok {
|
||||
ident.Obj = obj
|
||||
}
|
||||
if ident.Name != "_" {
|
||||
if debugResolve {
|
||||
r.trace("declaring %s@%v", ident.Name, ident.Pos())
|
||||
}
|
||||
if alt := scope.Insert(obj); alt != nil && r.declErr != nil {
|
||||
prevDecl := ""
|
||||
if pos := alt.Pos(); pos.IsValid() {
|
||||
prevDecl = r.sprintf("\n\tprevious declaration at %v", pos)
|
||||
}
|
||||
r.declErr(ident.Pos(), fmt.Sprintf("%s redeclared in this block%s", ident.Name, prevDecl))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *resolver) shortVarDecl(decl *ast.AssignStmt) {
|
||||
// Go spec: A short variable declaration may redeclare variables
|
||||
// provided they were originally declared in the same block with
|
||||
// the same type, and at least one of the non-blank variables is new.
|
||||
n := 0 // number of new variables
|
||||
for _, x := range decl.Lhs {
|
||||
if ident, isIdent := x.(*ast.Ident); isIdent {
|
||||
assert(ident.Obj == nil, "identifier already declared or resolved")
|
||||
obj := ast.NewObj(ast.Var, ident.Name)
|
||||
// remember corresponding assignment for other tools
|
||||
obj.Decl = decl
|
||||
ident.Obj = obj
|
||||
if ident.Name != "_" {
|
||||
if debugResolve {
|
||||
r.trace("declaring %s@%v", ident.Name, ident.Pos())
|
||||
}
|
||||
if alt := r.topScope.Insert(obj); alt != nil {
|
||||
ident.Obj = alt // redeclaration
|
||||
} else {
|
||||
n++ // new declaration
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if n == 0 && r.declErr != nil {
|
||||
r.declErr(decl.Lhs[0].Pos(), "no new variables on left side of :=")
|
||||
}
|
||||
}
|
||||
|
||||
// The unresolved object is a sentinel to mark identifiers that have been added
|
||||
// to the list of unresolved identifiers. The sentinel is only used for verifying
|
||||
// internal consistency.
|
||||
var unresolved = new(ast.Object)
|
||||
|
||||
// If x is an identifier, resolve attempts to resolve x by looking up
|
||||
// the object it denotes. If no object is found and collectUnresolved is
|
||||
// set, x is marked as unresolved and collected in the list of unresolved
|
||||
// identifiers.
|
||||
func (r *resolver) resolve(ident *ast.Ident, collectUnresolved bool) {
|
||||
if ident.Obj != nil {
|
||||
panic(r.sprintf("%v: identifier %s already declared or resolved", ident.Pos(), ident.Name))
|
||||
}
|
||||
// '_' should never refer to existing declarations, because it has special
|
||||
// handling in the spec.
|
||||
if ident.Name == "_" {
|
||||
return
|
||||
}
|
||||
for s := r.topScope; s != nil; s = s.Outer {
|
||||
if obj := s.Lookup(ident.Name); obj != nil {
|
||||
if debugResolve {
|
||||
r.trace("resolved %v:%s to %v", ident.Pos(), ident.Name, obj)
|
||||
}
|
||||
assert(obj.Name != "", "obj with no name")
|
||||
// Identifiers (for receiver type parameters) are written to the scope,
|
||||
// but never set as the resolved object. See issue #50956.
|
||||
if _, ok := obj.Decl.(*ast.Ident); !ok {
|
||||
ident.Obj = obj
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
// all local scopes are known, so any unresolved identifier
|
||||
// must be found either in the file scope, package scope
|
||||
// (perhaps in another file), or universe scope --- collect
|
||||
// them so that they can be resolved later
|
||||
if collectUnresolved {
|
||||
ident.Obj = unresolved
|
||||
r.unresolved = append(r.unresolved, ident)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *resolver) walkExprs(list []ast.Expr) {
|
||||
for _, node := range list {
|
||||
ast.Walk(r, node)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *resolver) walkLHS(list []ast.Expr) {
|
||||
for _, expr := range list {
|
||||
expr := unparen(expr)
|
||||
if _, ok := expr.(*ast.Ident); !ok && expr != nil {
|
||||
ast.Walk(r, expr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *resolver) walkStmts(list []ast.Stmt) {
|
||||
for _, stmt := range list {
|
||||
ast.Walk(r, stmt)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *resolver) Visit(node ast.Node) ast.Visitor {
|
||||
if debugResolve && node != nil {
|
||||
r.trace("node %T@%v", node, node.Pos())
|
||||
}
|
||||
|
||||
switch n := node.(type) {
|
||||
|
||||
// Expressions.
|
||||
case *ast.Ident:
|
||||
r.resolve(n, true)
|
||||
|
||||
case *ast.FuncLit:
|
||||
r.openScope(n.Pos())
|
||||
defer r.closeScope()
|
||||
r.walkFuncType(n.Type)
|
||||
r.walkBody(n.Body)
|
||||
|
||||
case *ast.SelectorExpr:
|
||||
ast.Walk(r, n.X)
|
||||
// Note: don't try to resolve n.Sel, as we don't support qualified
|
||||
// resolution.
|
||||
|
||||
case *ast.StructType:
|
||||
r.openScope(n.Pos())
|
||||
defer r.closeScope()
|
||||
r.walkFieldList(n.Fields, ast.Var)
|
||||
|
||||
case *ast.FuncType:
|
||||
r.openScope(n.Pos())
|
||||
defer r.closeScope()
|
||||
r.walkFuncType(n)
|
||||
|
||||
case *ast.CompositeLit:
|
||||
if n.Type != nil {
|
||||
ast.Walk(r, n.Type)
|
||||
}
|
||||
for _, e := range n.Elts {
|
||||
if kv, _ := e.(*ast.KeyValueExpr); kv != nil {
|
||||
// See issue #45160: try to resolve composite lit keys, but don't
|
||||
// collect them as unresolved if resolution failed. This replicates
|
||||
// existing behavior when resolving during parsing.
|
||||
if ident, _ := kv.Key.(*ast.Ident); ident != nil {
|
||||
r.resolve(ident, false)
|
||||
} else {
|
||||
ast.Walk(r, kv.Key)
|
||||
}
|
||||
ast.Walk(r, kv.Value)
|
||||
} else {
|
||||
ast.Walk(r, e)
|
||||
}
|
||||
}
|
||||
|
||||
case *ast.InterfaceType:
|
||||
r.openScope(n.Pos())
|
||||
defer r.closeScope()
|
||||
r.walkFieldList(n.Methods, ast.Fun)
|
||||
|
||||
// Statements
|
||||
case *ast.LabeledStmt:
|
||||
r.declare(n, nil, r.labelScope, ast.Lbl, n.Label)
|
||||
ast.Walk(r, n.Stmt)
|
||||
|
||||
case *ast.AssignStmt:
|
||||
r.walkExprs(n.Rhs)
|
||||
if n.Tok == token.DEFINE {
|
||||
r.shortVarDecl(n)
|
||||
} else {
|
||||
r.walkExprs(n.Lhs)
|
||||
}
|
||||
|
||||
case *ast.BranchStmt:
|
||||
// add to list of unresolved targets
|
||||
if n.Tok != token.FALLTHROUGH && n.Label != nil {
|
||||
depth := len(r.targetStack) - 1
|
||||
r.targetStack[depth] = append(r.targetStack[depth], n.Label)
|
||||
}
|
||||
|
||||
case *ast.BlockStmt:
|
||||
r.openScope(n.Pos())
|
||||
defer r.closeScope()
|
||||
r.walkStmts(n.List)
|
||||
|
||||
case *ast.IfStmt:
|
||||
r.openScope(n.Pos())
|
||||
defer r.closeScope()
|
||||
if n.Init != nil {
|
||||
ast.Walk(r, n.Init)
|
||||
}
|
||||
ast.Walk(r, n.Cond)
|
||||
ast.Walk(r, n.Body)
|
||||
if n.Else != nil {
|
||||
ast.Walk(r, n.Else)
|
||||
}
|
||||
|
||||
case *ast.CaseClause:
|
||||
r.walkExprs(n.List)
|
||||
r.openScope(n.Pos())
|
||||
defer r.closeScope()
|
||||
r.walkStmts(n.Body)
|
||||
|
||||
case *ast.SwitchStmt:
|
||||
r.openScope(n.Pos())
|
||||
defer r.closeScope()
|
||||
if n.Init != nil {
|
||||
ast.Walk(r, n.Init)
|
||||
}
|
||||
if n.Tag != nil {
|
||||
// The scope below reproduces some unnecessary behavior of the parser,
|
||||
// opening an extra scope in case this is a type switch. It's not needed
|
||||
// for expression switches.
|
||||
// TODO: remove this once we've matched the parser resolution exactly.
|
||||
if n.Init != nil {
|
||||
r.openScope(n.Tag.Pos())
|
||||
defer r.closeScope()
|
||||
}
|
||||
ast.Walk(r, n.Tag)
|
||||
}
|
||||
if n.Body != nil {
|
||||
r.walkStmts(n.Body.List)
|
||||
}
|
||||
|
||||
case *ast.TypeSwitchStmt:
|
||||
if n.Init != nil {
|
||||
r.openScope(n.Pos())
|
||||
defer r.closeScope()
|
||||
ast.Walk(r, n.Init)
|
||||
}
|
||||
r.openScope(n.Assign.Pos())
|
||||
defer r.closeScope()
|
||||
ast.Walk(r, n.Assign)
|
||||
// s.Body consists only of case clauses, so does not get its own
|
||||
// scope.
|
||||
if n.Body != nil {
|
||||
r.walkStmts(n.Body.List)
|
||||
}
|
||||
|
||||
case *ast.CommClause:
|
||||
r.openScope(n.Pos())
|
||||
defer r.closeScope()
|
||||
if n.Comm != nil {
|
||||
ast.Walk(r, n.Comm)
|
||||
}
|
||||
r.walkStmts(n.Body)
|
||||
|
||||
case *ast.SelectStmt:
|
||||
// as for switch statements, select statement bodies don't get their own
|
||||
// scope.
|
||||
if n.Body != nil {
|
||||
r.walkStmts(n.Body.List)
|
||||
}
|
||||
|
||||
case *ast.ForStmt:
|
||||
r.openScope(n.Pos())
|
||||
defer r.closeScope()
|
||||
if n.Init != nil {
|
||||
ast.Walk(r, n.Init)
|
||||
}
|
||||
if n.Cond != nil {
|
||||
ast.Walk(r, n.Cond)
|
||||
}
|
||||
if n.Post != nil {
|
||||
ast.Walk(r, n.Post)
|
||||
}
|
||||
ast.Walk(r, n.Body)
|
||||
|
||||
case *ast.RangeStmt:
|
||||
r.openScope(n.Pos())
|
||||
defer r.closeScope()
|
||||
ast.Walk(r, n.X)
|
||||
var lhs []ast.Expr
|
||||
if n.Key != nil {
|
||||
lhs = append(lhs, n.Key)
|
||||
}
|
||||
if n.Value != nil {
|
||||
lhs = append(lhs, n.Value)
|
||||
}
|
||||
if len(lhs) > 0 {
|
||||
if n.Tok == token.DEFINE {
|
||||
// Note: we can't exactly match the behavior of object resolution
|
||||
// during the parsing pass here, as it uses the position of the RANGE
|
||||
// token for the RHS OpPos. That information is not contained within
|
||||
// the AST.
|
||||
as := &ast.AssignStmt{
|
||||
Lhs: lhs,
|
||||
Tok: token.DEFINE,
|
||||
TokPos: n.TokPos,
|
||||
Rhs: []ast.Expr{&ast.UnaryExpr{Op: token.RANGE, X: n.X}},
|
||||
}
|
||||
// TODO(rFindley): this walkLHS reproduced the parser resolution, but
|
||||
// is it necessary? By comparison, for a normal AssignStmt we don't
|
||||
// walk the LHS in case there is an invalid identifier list.
|
||||
r.walkLHS(lhs)
|
||||
r.shortVarDecl(as)
|
||||
} else {
|
||||
r.walkExprs(lhs)
|
||||
}
|
||||
}
|
||||
ast.Walk(r, n.Body)
|
||||
|
||||
// Declarations
|
||||
case *ast.GenDecl:
|
||||
switch n.Tok {
|
||||
case token.CONST, token.VAR:
|
||||
for i, spec := range n.Specs {
|
||||
spec := spec.(*ast.ValueSpec)
|
||||
kind := ast.Con
|
||||
if n.Tok == token.VAR {
|
||||
kind = ast.Var
|
||||
}
|
||||
r.walkExprs(spec.Values)
|
||||
if spec.Type != nil {
|
||||
ast.Walk(r, spec.Type)
|
||||
}
|
||||
r.declare(spec, i, r.topScope, kind, spec.Names...)
|
||||
}
|
||||
case token.TYPE:
|
||||
for _, spec := range n.Specs {
|
||||
spec := spec.(*ast.TypeSpec)
|
||||
// Go spec: The scope of a type identifier declared inside a function begins
|
||||
// at the identifier in the TypeSpec and ends at the end of the innermost
|
||||
// containing block.
|
||||
r.declare(spec, nil, r.topScope, ast.Typ, spec.Name)
|
||||
if spec.TypeParams != nil {
|
||||
r.openScope(spec.Pos())
|
||||
r.walkTParams(spec.TypeParams)
|
||||
r.closeScope()
|
||||
}
|
||||
ast.Walk(r, spec.Type)
|
||||
}
|
||||
}
|
||||
|
||||
case *ast.FuncDecl:
|
||||
// Open the function scope.
|
||||
r.openScope(n.Pos())
|
||||
defer r.closeScope()
|
||||
|
||||
r.walkRecv(n.Recv)
|
||||
|
||||
// Type parameters are walked normally: they can reference each other, and
|
||||
// can be referenced by normal parameters.
|
||||
if n.Type.TypeParams != nil {
|
||||
r.walkTParams(n.Type.TypeParams)
|
||||
// TODO(rFindley): need to address receiver type parameters.
|
||||
}
|
||||
|
||||
// Resolve and declare parameters in a specific order to get duplicate
|
||||
// declaration errors in the correct location.
|
||||
r.resolveList(n.Type.Params)
|
||||
r.resolveList(n.Type.Results)
|
||||
r.declareList(n.Recv, ast.Var)
|
||||
r.declareList(n.Type.Params, ast.Var)
|
||||
r.declareList(n.Type.Results, ast.Var)
|
||||
|
||||
r.walkBody(n.Body)
|
||||
if n.Recv == nil && n.Name.Name != "init" {
|
||||
r.declare(n, nil, r.pkgScope, ast.Fun, n.Name)
|
||||
}
|
||||
|
||||
default:
|
||||
return r
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *resolver) walkFuncType(typ *ast.FuncType) {
|
||||
// typ.TypeParams must be walked separately for FuncDecls.
|
||||
r.resolveList(typ.Params)
|
||||
r.resolveList(typ.Results)
|
||||
r.declareList(typ.Params, ast.Var)
|
||||
r.declareList(typ.Results, ast.Var)
|
||||
}
|
||||
|
||||
func (r *resolver) resolveList(list *ast.FieldList) {
|
||||
if list == nil {
|
||||
return
|
||||
}
|
||||
for _, f := range list.List {
|
||||
if f.Type != nil {
|
||||
ast.Walk(r, f.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *resolver) declareList(list *ast.FieldList, kind ast.ObjKind) {
|
||||
if list == nil {
|
||||
return
|
||||
}
|
||||
for _, f := range list.List {
|
||||
r.declare(f, nil, r.topScope, kind, f.Names...)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *resolver) walkRecv(recv *ast.FieldList) {
|
||||
// If our receiver has receiver type parameters, we must declare them before
|
||||
// trying to resolve the rest of the receiver, and avoid re-resolving the
|
||||
// type parameter identifiers.
|
||||
if recv == nil || len(recv.List) == 0 {
|
||||
return // nothing to do
|
||||
}
|
||||
typ := recv.List[0].Type
|
||||
if ptr, ok := typ.(*ast.StarExpr); ok {
|
||||
typ = ptr.X
|
||||
}
|
||||
|
||||
var declareExprs []ast.Expr // exprs to declare
|
||||
var resolveExprs []ast.Expr // exprs to resolve
|
||||
switch typ := typ.(type) {
|
||||
case *ast.IndexExpr:
|
||||
declareExprs = []ast.Expr{typ.Index}
|
||||
resolveExprs = append(resolveExprs, typ.X)
|
||||
case *ast.IndexListExpr:
|
||||
declareExprs = typ.Indices
|
||||
resolveExprs = append(resolveExprs, typ.X)
|
||||
default:
|
||||
resolveExprs = append(resolveExprs, typ)
|
||||
}
|
||||
for _, expr := range declareExprs {
|
||||
if id, _ := expr.(*ast.Ident); id != nil {
|
||||
r.declare(expr, nil, r.topScope, ast.Typ, id)
|
||||
} else {
|
||||
// The receiver type parameter expression is invalid, but try to resolve
|
||||
// it anyway for consistency.
|
||||
resolveExprs = append(resolveExprs, expr)
|
||||
}
|
||||
}
|
||||
for _, expr := range resolveExprs {
|
||||
if expr != nil {
|
||||
ast.Walk(r, expr)
|
||||
}
|
||||
}
|
||||
// The receiver is invalid, but try to resolve it anyway for consistency.
|
||||
for _, f := range recv.List[1:] {
|
||||
if f.Type != nil {
|
||||
ast.Walk(r, f.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *resolver) walkFieldList(list *ast.FieldList, kind ast.ObjKind) {
|
||||
if list == nil {
|
||||
return
|
||||
}
|
||||
r.resolveList(list)
|
||||
r.declareList(list, kind)
|
||||
}
|
||||
|
||||
// walkTParams is like walkFieldList, but declares type parameters eagerly so
|
||||
// that they may be resolved in the constraint expressions held in the field
|
||||
// Type.
|
||||
func (r *resolver) walkTParams(list *ast.FieldList) {
|
||||
r.declareList(list, ast.Typ)
|
||||
r.resolveList(list)
|
||||
}
|
||||
|
||||
func (r *resolver) walkBody(body *ast.BlockStmt) {
|
||||
if body == nil {
|
||||
return
|
||||
}
|
||||
r.openLabelScope()
|
||||
defer r.closeLabelScope()
|
||||
r.walkStmts(body.List)
|
||||
}
|
||||
@@ -758,6 +758,10 @@ type none struct{}
|
||||
|
||||
var hasAltPkg = map[string]none{
|
||||
"crypto/md5": {},
|
||||
"crypto/sha1": {},
|
||||
"crypto/sha256": {},
|
||||
"crypto/sha512": {},
|
||||
"crypto/rand": {},
|
||||
"fmt": {},
|
||||
"hash/crc32": {},
|
||||
"internal/abi": {},
|
||||
@@ -768,6 +772,7 @@ var hasAltPkg = map[string]none{
|
||||
"internal/syscall/execenv": {},
|
||||
"internal/syscall/unix": {},
|
||||
"math": {},
|
||||
"math/big": {},
|
||||
"math/cmplx": {},
|
||||
"math/rand": {},
|
||||
"reflect": {},
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
)
|
||||
|
||||
//go:embed _overlay/go/parser/resolver.go
|
||||
var go_parser_resolver string
|
||||
|
||||
var overlayFiles = map[string]string{
|
||||
"math/exp_amd64.go": "package math;",
|
||||
"go/parser/resolver.go": go_parser_resolver,
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package md5
|
||||
|
||||
// llgo:skipall
|
||||
import (
|
||||
"crypto"
|
||||
"hash"
|
||||
"unsafe"
|
||||
|
||||
@@ -25,6 +26,10 @@ import (
|
||||
"github.com/goplus/llgo/c/openssl"
|
||||
)
|
||||
|
||||
func init() {
|
||||
crypto.RegisterHash(crypto.MD5, New)
|
||||
}
|
||||
|
||||
// The blocksize of MD5 in bytes.
|
||||
const BlockSize = 64
|
||||
|
||||
|
||||
105
internal/lib/crypto/rand/rand.go
Normal file
105
internal/lib/crypto/rand/rand.go
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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 rand
|
||||
|
||||
// llgo:skipall
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/goplus/llgo/c"
|
||||
"github.com/goplus/llgo/c/openssl"
|
||||
"github.com/qiniu/x/errors"
|
||||
)
|
||||
|
||||
type rndReader struct{}
|
||||
|
||||
func (rndReader) Read(p []byte) (n int, err error) {
|
||||
return Read(p)
|
||||
}
|
||||
|
||||
// Reader is a global, shared instance of a cryptographically
|
||||
// secure random number generator.
|
||||
var Reader io.Reader = rndReader{}
|
||||
|
||||
type opensslError struct {
|
||||
file *c.Char
|
||||
line c.Int
|
||||
flags c.Int
|
||||
function *c.Char
|
||||
data *c.Char
|
||||
err openssl.Errno
|
||||
}
|
||||
|
||||
// [error code]:[library name]:[function name]:[reason string]:[[filename:line]]: [text message]
|
||||
func (p *opensslError) Error() string {
|
||||
const bufsize = 1024
|
||||
buf := (*c.Char)(c.Alloca(bufsize))
|
||||
lib := openssl.ERRLibErrorString(p.err)
|
||||
reason := openssl.ERRReasonErrorString(p.err)
|
||||
n := uintptr(c.Snprintf(
|
||||
buf, bufsize,
|
||||
c.Str("%d:%s:%s:%s:[%s:%d]: "),
|
||||
p.err, lib, p.function, reason, p.file, p.line))
|
||||
n += c.Strlen(openssl.ERRErrorString(p.err, c.Advance(buf, n)))
|
||||
return c.GoString(buf, n)
|
||||
}
|
||||
|
||||
func getError() *opensslError {
|
||||
ret := new(opensslError)
|
||||
err := openssl.ERRGetErrorAll(&ret.file, &ret.line, &ret.function, &ret.data, &ret.flags)
|
||||
if err == 0 {
|
||||
return nil
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func getErrors() error {
|
||||
var errs errors.List
|
||||
for openssl.ERRPeekError() != 0 {
|
||||
errs.Add(getError())
|
||||
}
|
||||
return errs.ToError()
|
||||
}
|
||||
|
||||
// Read is a helper function that calls Reader.Read using io.ReadFull.
|
||||
// On return, n == len(b) if and only if err == nil.
|
||||
func Read(b []byte) (n int, err error) {
|
||||
if openssl.RANDBytes(b) != 0 {
|
||||
return len(b), nil
|
||||
}
|
||||
return 0, getErrors()
|
||||
}
|
||||
|
||||
/* TODO(xsw):
|
||||
// batched returns a function that calls f to populate a []byte by chunking it
|
||||
// into subslices of, at most, readMax bytes.
|
||||
func batched(f func([]byte) error, readMax int) func([]byte) error {
|
||||
return func(out []byte) error {
|
||||
for len(out) > 0 {
|
||||
read := len(out)
|
||||
if read > readMax {
|
||||
read = readMax
|
||||
}
|
||||
if err := f(out[:read]); err != nil {
|
||||
return err
|
||||
}
|
||||
out = out[read:]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
*/
|
||||
98
internal/lib/crypto/rand/util.go
Normal file
98
internal/lib/crypto/rand/util.go
Normal file
@@ -0,0 +1,98 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package rand
|
||||
|
||||
/* TODO(xsw):
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// Prime returns a number of the given bit length that is prime with high probability.
|
||||
// Prime will return error for any error returned by rand.Read or if bits < 2.
|
||||
func Prime(rand io.Reader, bits int) (*big.Int, error) {
|
||||
if bits < 2 {
|
||||
return nil, errors.New("crypto/rand: prime size must be at least 2-bit")
|
||||
}
|
||||
|
||||
b := uint(bits % 8)
|
||||
if b == 0 {
|
||||
b = 8
|
||||
}
|
||||
|
||||
bytes := make([]byte, (bits+7)/8)
|
||||
p := new(big.Int)
|
||||
|
||||
for {
|
||||
if _, err := io.ReadFull(rand, bytes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Clear bits in the first byte to make sure the candidate has a size <= bits.
|
||||
bytes[0] &= uint8(int(1<<b) - 1)
|
||||
// Don't let the value be too small, i.e, set the most significant two bits.
|
||||
// Setting the top two bits, rather than just the top bit,
|
||||
// means that when two of these values are multiplied together,
|
||||
// the result isn't ever one bit short.
|
||||
if b >= 2 {
|
||||
bytes[0] |= 3 << (b - 2)
|
||||
} else {
|
||||
// Here b==1, because b cannot be zero.
|
||||
bytes[0] |= 1
|
||||
if len(bytes) > 1 {
|
||||
bytes[1] |= 0x80
|
||||
}
|
||||
}
|
||||
// Make the value odd since an even number this large certainly isn't prime.
|
||||
bytes[len(bytes)-1] |= 1
|
||||
|
||||
p.SetBytes(bytes)
|
||||
if p.ProbablyPrime(20) {
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Int returns a uniform random value in [0, max). It panics if max <= 0.
|
||||
func Int(rand io.Reader, max *big.Int) (n *big.Int, err error) {
|
||||
if max.Sign() <= 0 {
|
||||
panic("crypto/rand: argument to Int is <= 0")
|
||||
}
|
||||
n = new(big.Int)
|
||||
n.Sub(max, n.SetUint64(1))
|
||||
// bitLen is the maximum bit length needed to encode a value < max.
|
||||
bitLen := n.BitLen()
|
||||
if bitLen == 0 {
|
||||
// the only valid result is 0
|
||||
return
|
||||
}
|
||||
// k is the maximum byte length needed to encode a value < max.
|
||||
k := (bitLen + 7) / 8
|
||||
// b is the number of bits in the most significant byte of max-1.
|
||||
b := uint(bitLen % 8)
|
||||
if b == 0 {
|
||||
b = 8
|
||||
}
|
||||
|
||||
bytes := make([]byte, k)
|
||||
|
||||
for {
|
||||
_, err = io.ReadFull(rand, bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Clear bits in the first byte to increase the probability
|
||||
// that the candidate is < max.
|
||||
bytes[0] &= uint8(int(1<<b) - 1)
|
||||
|
||||
n.SetBytes(bytes)
|
||||
if n.Cmp(max) < 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
57
internal/lib/crypto/sha1/sha1.go
Normal file
57
internal/lib/crypto/sha1/sha1.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package sha1
|
||||
|
||||
// llgo:skipall
|
||||
import (
|
||||
"crypto"
|
||||
"hash"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goplus/llgo/c"
|
||||
"github.com/goplus/llgo/c/openssl"
|
||||
)
|
||||
|
||||
func init() {
|
||||
crypto.RegisterHash(crypto.SHA1, New)
|
||||
}
|
||||
|
||||
// The blocksize of SHA-1 in bytes.
|
||||
const BlockSize = 64
|
||||
|
||||
// The size of a SHA-1 checksum in bytes.
|
||||
const Size = 20
|
||||
|
||||
type digest struct {
|
||||
ctx openssl.SHA_CTX
|
||||
}
|
||||
|
||||
func (d *digest) Size() int { return Size }
|
||||
|
||||
func (d *digest) BlockSize() int { return BlockSize }
|
||||
|
||||
func (d *digest) Reset() {
|
||||
d.ctx.Init()
|
||||
}
|
||||
|
||||
func (d *digest) Write(p []byte) (nn int, err error) {
|
||||
d.ctx.UpdateBytes(p)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (d *digest) Sum(in []byte) []byte {
|
||||
hash := (*[Size]byte)(c.Alloca(Size))
|
||||
d.ctx.Final((*byte)(unsafe.Pointer(hash)))
|
||||
return append(in, hash[:]...)
|
||||
}
|
||||
|
||||
// New returns a new hash.Hash computing the SHA1 checksum.
|
||||
func New() hash.Hash {
|
||||
d := new(digest)
|
||||
d.ctx.Init()
|
||||
return d
|
||||
}
|
||||
|
||||
// Sum returns the SHA-1 checksum of the data.
|
||||
func Sum(data []byte) (ret [Size]byte) {
|
||||
openssl.SHA1Bytes(data, &ret[0])
|
||||
return
|
||||
}
|
||||
48
internal/lib/crypto/sha256/sha224.go
Normal file
48
internal/lib/crypto/sha256/sha224.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package sha256
|
||||
|
||||
import (
|
||||
"hash"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goplus/llgo/c"
|
||||
"github.com/goplus/llgo/c/openssl"
|
||||
)
|
||||
|
||||
// The size of a SHA224 checksum in bytes.
|
||||
const Size224 = 28
|
||||
|
||||
type digest224 struct {
|
||||
ctx openssl.SHA224_CTX
|
||||
}
|
||||
|
||||
func (d *digest224) Size() int { return Size224 }
|
||||
|
||||
func (d *digest224) BlockSize() int { return BlockSize }
|
||||
|
||||
func (d *digest224) Reset() {
|
||||
d.ctx.Init()
|
||||
}
|
||||
|
||||
func (d *digest224) Write(p []byte) (nn int, err error) {
|
||||
d.ctx.UpdateBytes(p)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (d *digest224) Sum(in []byte) []byte {
|
||||
hash := (*[Size]byte)(c.Alloca(Size))
|
||||
d.ctx.Final((*byte)(unsafe.Pointer(hash)))
|
||||
return append(in, hash[:]...)
|
||||
}
|
||||
|
||||
// New224 returns a new hash.Hash computing the SHA224 checksum.
|
||||
func New224() hash.Hash {
|
||||
d := new(digest224)
|
||||
d.ctx.Init()
|
||||
return d
|
||||
}
|
||||
|
||||
// Sum224 returns the SHA224 checksum of the data.
|
||||
func Sum224(data []byte) (ret [Size224]byte) {
|
||||
openssl.SHA224Bytes(data, &ret[0])
|
||||
return
|
||||
}
|
||||
58
internal/lib/crypto/sha256/sha256.go
Normal file
58
internal/lib/crypto/sha256/sha256.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package sha256
|
||||
|
||||
// llgo:skipall
|
||||
import (
|
||||
"crypto"
|
||||
"hash"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goplus/llgo/c"
|
||||
"github.com/goplus/llgo/c/openssl"
|
||||
)
|
||||
|
||||
func init() {
|
||||
crypto.RegisterHash(crypto.SHA224, New224)
|
||||
crypto.RegisterHash(crypto.SHA256, New)
|
||||
}
|
||||
|
||||
// The blocksize of SHA256 and SHA224 in bytes.
|
||||
const BlockSize = 64
|
||||
|
||||
// The size of a SHA256 checksum in bytes.
|
||||
const Size = 32
|
||||
|
||||
type digest256 struct {
|
||||
ctx openssl.SHA256_CTX
|
||||
}
|
||||
|
||||
func (d *digest256) Size() int { return Size }
|
||||
|
||||
func (d *digest256) BlockSize() int { return BlockSize }
|
||||
|
||||
func (d *digest256) Reset() {
|
||||
d.ctx.Init()
|
||||
}
|
||||
|
||||
func (d *digest256) Write(p []byte) (nn int, err error) {
|
||||
d.ctx.UpdateBytes(p)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (d *digest256) Sum(in []byte) []byte {
|
||||
hash := (*[Size]byte)(c.Alloca(Size))
|
||||
d.ctx.Final((*byte)(unsafe.Pointer(hash)))
|
||||
return append(in, hash[:]...)
|
||||
}
|
||||
|
||||
// New returns a new hash.Hash computing the SHA256 checksum.
|
||||
func New() hash.Hash {
|
||||
d := new(digest256)
|
||||
d.ctx.Init()
|
||||
return d
|
||||
}
|
||||
|
||||
// Sum256 returns the SHA256 checksum of the data.
|
||||
func Sum256(data []byte) (ret [Size]byte) {
|
||||
openssl.SHA256Bytes(data, &ret[0])
|
||||
return
|
||||
}
|
||||
43
internal/lib/crypto/sha512/sha384.go
Normal file
43
internal/lib/crypto/sha512/sha384.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package sha512
|
||||
|
||||
import (
|
||||
"hash"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goplus/llgo/c"
|
||||
"github.com/goplus/llgo/c/openssl"
|
||||
)
|
||||
|
||||
type digest384 struct {
|
||||
ctx openssl.SHA384_CTX
|
||||
}
|
||||
|
||||
func (d *digest384) Size() int { return Size384 }
|
||||
|
||||
func (d *digest384) BlockSize() int { return BlockSize }
|
||||
|
||||
func (d *digest384) Reset() {
|
||||
d.ctx.Init()
|
||||
}
|
||||
|
||||
func (d *digest384) Write(p []byte) (nn int, err error) {
|
||||
d.ctx.UpdateBytes(p)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (d *digest384) Sum(in []byte) []byte {
|
||||
hash := (*[Size]byte)(c.Alloca(Size))
|
||||
d.ctx.Final((*byte)(unsafe.Pointer(hash)))
|
||||
return append(in, hash[:]...)
|
||||
}
|
||||
|
||||
func New384() hash.Hash {
|
||||
d := new(digest384)
|
||||
d.ctx.Init()
|
||||
return d
|
||||
}
|
||||
|
||||
func Sum384(data []byte) (ret [Size384]byte) {
|
||||
openssl.SHA384Bytes(data, &ret[0])
|
||||
return
|
||||
}
|
||||
86
internal/lib/crypto/sha512/sha512.go
Normal file
86
internal/lib/crypto/sha512/sha512.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package sha512
|
||||
|
||||
// llgo:skipall
|
||||
import (
|
||||
"crypto"
|
||||
"hash"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goplus/llgo/c"
|
||||
"github.com/goplus/llgo/c/openssl"
|
||||
)
|
||||
|
||||
func init() {
|
||||
crypto.RegisterHash(crypto.SHA384, New384)
|
||||
crypto.RegisterHash(crypto.SHA512, New)
|
||||
crypto.RegisterHash(crypto.SHA512_224, New512_224)
|
||||
crypto.RegisterHash(crypto.SHA512_256, New512_256)
|
||||
}
|
||||
|
||||
const (
|
||||
// Size is the size, in bytes, of a SHA-512 checksum.
|
||||
Size = 64
|
||||
|
||||
// Size224 is the size, in bytes, of a SHA-512/224 checksum.
|
||||
Size224 = 28
|
||||
|
||||
// Size256 is the size, in bytes, of a SHA-512/256 checksum.
|
||||
Size256 = 32
|
||||
|
||||
// Size384 is the size, in bytes, of a SHA-384 checksum.
|
||||
Size384 = 48
|
||||
|
||||
// BlockSize is the block size, in bytes, of the SHA-512/224,
|
||||
// SHA-512/256, SHA-384 and SHA-512 hash functions.
|
||||
BlockSize = 128
|
||||
)
|
||||
|
||||
type digest512 struct {
|
||||
ctx openssl.SHA512_CTX
|
||||
}
|
||||
|
||||
func (d *digest512) Size() int { return Size }
|
||||
|
||||
func (d *digest512) BlockSize() int { return BlockSize }
|
||||
|
||||
func (d *digest512) Reset() {
|
||||
d.ctx.Init()
|
||||
}
|
||||
|
||||
func (d *digest512) Write(p []byte) (nn int, err error) {
|
||||
d.ctx.UpdateBytes(p)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (d *digest512) Sum(in []byte) []byte {
|
||||
hash := (*[Size]byte)(c.Alloca(Size))
|
||||
d.ctx.Final((*byte)(unsafe.Pointer(hash)))
|
||||
return append(in, hash[:]...)
|
||||
}
|
||||
|
||||
func New() hash.Hash {
|
||||
d := new(digest512)
|
||||
d.ctx.Init()
|
||||
return d
|
||||
}
|
||||
|
||||
func Sum512(data []byte) (ret [Size]byte) {
|
||||
openssl.SHA512Bytes(data, &ret[0])
|
||||
return
|
||||
}
|
||||
|
||||
func New512_224() hash.Hash {
|
||||
panic("todo: crypto/sha512.New512_224")
|
||||
}
|
||||
|
||||
func Sum512_224(data []byte) [Size224]byte {
|
||||
panic("todo: crypto/sha512.Sum512_224")
|
||||
}
|
||||
|
||||
func New512_256() hash.Hash {
|
||||
panic("todo: crypto/sha512.New512_256")
|
||||
}
|
||||
|
||||
func Sum512_256(data []byte) [Size256]byte {
|
||||
panic("todo: crypto/sha512.Sum512_256")
|
||||
}
|
||||
398
internal/lib/math/big/int.go
Normal file
398
internal/lib/math/big/int.go
Normal file
@@ -0,0 +1,398 @@
|
||||
/*
|
||||
* 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 big
|
||||
|
||||
// llgo:skipall
|
||||
import (
|
||||
"github.com/goplus/llgo/c/openssl"
|
||||
)
|
||||
|
||||
// A Word represents a single digit of a multi-precision unsigned integer.
|
||||
type Word openssl.BN_ULONG
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// TODO(xsw): share ctx
|
||||
func ctxGet() *openssl.BN_CTX {
|
||||
return openssl.BN_CTXNew()
|
||||
}
|
||||
|
||||
func ctxPut(ctx *openssl.BN_CTX) {
|
||||
ctx.Free()
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type Int openssl.BIGNUM
|
||||
|
||||
// Sign returns:
|
||||
//
|
||||
// -1 if x < 0
|
||||
// 0 if x == 0
|
||||
// +1 if x > 0
|
||||
func (x *Int) Sign() int {
|
||||
a := (*openssl.BIGNUM)(x)
|
||||
if a.IsNegative() != 0 {
|
||||
return -1
|
||||
} else if a.IsZero() != 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
// SetInt64 sets z to x and returns z.
|
||||
func (z *Int) SetInt64(x int64) *Int {
|
||||
a := (*openssl.BIGNUM)(z)
|
||||
if x < 0 {
|
||||
a.SetWord(openssl.BN_ULONG(-x))
|
||||
a.SetNegative(1)
|
||||
} else {
|
||||
a.SetWord(openssl.BN_ULONG(x))
|
||||
a.SetNegative(0)
|
||||
}
|
||||
return z
|
||||
}
|
||||
|
||||
// SetUint64 sets z to x and returns z.
|
||||
func (z *Int) SetUint64(x uint64) *Int {
|
||||
a := (*openssl.BIGNUM)(z)
|
||||
a.SetWord(openssl.BN_ULONG(x))
|
||||
a.SetNegative(0)
|
||||
return z
|
||||
}
|
||||
|
||||
// NewInt allocates and returns a new Int set to x.
|
||||
func NewInt(x int64) *Int {
|
||||
z := (*Int)(openssl.BNNew())
|
||||
return z.SetInt64(x)
|
||||
}
|
||||
|
||||
/*
|
||||
// Set sets z to x and returns z.
|
||||
func (z *Int) Set(x *Int) *Int {
|
||||
}
|
||||
|
||||
// Bits provides raw (unchecked but fast) access to x by returning its
|
||||
// absolute value as a little-endian Word slice. The result and x share
|
||||
// the same underlying array.
|
||||
// Bits is intended to support implementation of missing low-level Int
|
||||
// functionality outside this package; it should be avoided otherwise.
|
||||
func (x *Int) Bits() []Word {
|
||||
}
|
||||
|
||||
// SetBits provides raw (unchecked but fast) access to z by setting its
|
||||
// value to abs, interpreted as a little-endian Word slice, and returning
|
||||
// z. The result and abs share the same underlying array.
|
||||
// SetBits is intended to support implementation of missing low-level Int
|
||||
// functionality outside this package; it should be avoided otherwise.
|
||||
func (z *Int) SetBits(abs []Word) *Int {
|
||||
}
|
||||
|
||||
// Abs sets z to |x| (the absolute value of x) and returns z.
|
||||
func (z *Int) Abs(x *Int) *Int {
|
||||
}
|
||||
|
||||
// Neg sets z to -x and returns z.
|
||||
func (z *Int) Neg(x *Int) *Int {
|
||||
}
|
||||
*/
|
||||
|
||||
// Add sets z to the sum x+y and returns z.
|
||||
func (z *Int) Add(x, y *Int) *Int {
|
||||
(*openssl.BIGNUM)(z).Add((*openssl.BIGNUM)(x), (*openssl.BIGNUM)(y))
|
||||
return z
|
||||
}
|
||||
|
||||
// Sub sets z to the difference x-y and returns z.
|
||||
func (z *Int) Sub(x, y *Int) *Int {
|
||||
(*openssl.BIGNUM)(z).Sub((*openssl.BIGNUM)(x), (*openssl.BIGNUM)(y))
|
||||
return z
|
||||
}
|
||||
|
||||
/*
|
||||
// Mul sets z to the product x*y and returns z.
|
||||
func (z *Int) Mul(x, y *Int) *Int {
|
||||
}
|
||||
|
||||
// MulRange sets z to the product of all integers
|
||||
// in the range [a, b] inclusively and returns z.
|
||||
// If a > b (empty range), the result is 1.
|
||||
func (z *Int) MulRange(a, b int64) *Int {
|
||||
}
|
||||
|
||||
// Binomial sets z to the binomial coefficient C(n, k) and returns z.
|
||||
func (z *Int) Binomial(n, k int64) *Int {
|
||||
}
|
||||
|
||||
// Quo sets z to the quotient x/y for y != 0 and returns z.
|
||||
// If y == 0, a division-by-zero run-time panic occurs.
|
||||
// Quo implements truncated division (like Go); see QuoRem for more details.
|
||||
func (z *Int) Quo(x, y *Int) *Int {
|
||||
}
|
||||
|
||||
// Rem sets z to the remainder x%y for y != 0 and returns z.
|
||||
// If y == 0, a division-by-zero run-time panic occurs.
|
||||
// Rem implements truncated modulus (like Go); see QuoRem for more details.
|
||||
func (z *Int) Rem(x, y *Int) *Int {
|
||||
}
|
||||
|
||||
// QuoRem sets z to the quotient x/y and r to the remainder x%y
|
||||
// and returns the pair (z, r) for y != 0.
|
||||
// If y == 0, a division-by-zero run-time panic occurs.
|
||||
//
|
||||
// QuoRem implements T-division and modulus (like Go):
|
||||
//
|
||||
// q = x/y with the result truncated to zero
|
||||
// r = x - y*q
|
||||
//
|
||||
// (See Daan Leijen, “Division and Modulus for Computer Scientists”.)
|
||||
// See DivMod for Euclidean division and modulus (unlike Go).
|
||||
func (z *Int) QuoRem(x, y, r *Int) (*Int, *Int) {
|
||||
}
|
||||
|
||||
// Div sets z to the quotient x/y for y != 0 and returns z.
|
||||
// If y == 0, a division-by-zero run-time panic occurs.
|
||||
// Div implements Euclidean division (unlike Go); see DivMod for more details.
|
||||
func (z *Int) Div(x, y *Int) *Int {
|
||||
}
|
||||
|
||||
// Mod sets z to the modulus x%y for y != 0 and returns z.
|
||||
// If y == 0, a division-by-zero run-time panic occurs.
|
||||
// Mod implements Euclidean modulus (unlike Go); see DivMod for more details.
|
||||
func (z *Int) Mod(x, y *Int) *Int {
|
||||
}
|
||||
|
||||
// DivMod sets z to the quotient x div y and m to the modulus x mod y
|
||||
// and returns the pair (z, m) for y != 0.
|
||||
// If y == 0, a division-by-zero run-time panic occurs.
|
||||
//
|
||||
// DivMod implements Euclidean division and modulus (unlike Go):
|
||||
//
|
||||
// q = x div y such that
|
||||
// m = x - y*q with 0 <= m < |y|
|
||||
//
|
||||
// (See Raymond T. Boute, “The Euclidean definition of the functions
|
||||
// div and mod”. ACM Transactions on Programming Languages and
|
||||
// Systems (TOPLAS), 14(2):127-144, New York, NY, USA, 4/1992.
|
||||
// ACM press.)
|
||||
// See QuoRem for T-division and modulus (like Go).
|
||||
func (z *Int) DivMod(x, y, m *Int) (*Int, *Int) {
|
||||
}
|
||||
*/
|
||||
|
||||
// Cmp compares x and y and returns:
|
||||
//
|
||||
// -1 if x < y
|
||||
// 0 if x == y
|
||||
// +1 if x > y
|
||||
func (x *Int) Cmp(y *Int) (r int) {
|
||||
return int((*openssl.BIGNUM)(x).Cmp((*openssl.BIGNUM)(y)))
|
||||
}
|
||||
|
||||
// CmpAbs compares the absolute values of x and y and returns:
|
||||
//
|
||||
// -1 if |x| < |y|
|
||||
// 0 if |x| == |y|
|
||||
// +1 if |x| > |y|
|
||||
func (x *Int) CmpAbs(y *Int) int {
|
||||
return int((*openssl.BIGNUM)(x).Ucmp((*openssl.BIGNUM)(y)))
|
||||
}
|
||||
|
||||
/*
|
||||
// Int64 returns the int64 representation of x.
|
||||
// If x cannot be represented in an int64, the result is undefined.
|
||||
func (x *Int) Int64() int64 {
|
||||
}
|
||||
|
||||
// Uint64 returns the uint64 representation of x.
|
||||
// If x cannot be represented in a uint64, the result is undefined.
|
||||
func (x *Int) Uint64() uint64 {
|
||||
}
|
||||
|
||||
// IsInt64 reports whether x can be represented as an int64.
|
||||
func (x *Int) IsInt64() bool {
|
||||
}
|
||||
|
||||
// IsUint64 reports whether x can be represented as a uint64.
|
||||
func (x *Int) IsUint64() bool {
|
||||
}
|
||||
|
||||
// Float64 returns the float64 value nearest x,
|
||||
// and an indication of any rounding that occurred.
|
||||
// TODO(xsw):
|
||||
// func (x *Int) Float64() (float64, Accuracy)
|
||||
|
||||
// SetString sets z to the value of s, interpreted in the given base,
|
||||
// and returns z and a boolean indicating success. The entire string
|
||||
// (not just a prefix) must be valid for success. If SetString fails,
|
||||
// the value of z is undefined but the returned value is nil.
|
||||
//
|
||||
// The base argument must be 0 or a value between 2 and MaxBase.
|
||||
// For base 0, the number prefix determines the actual base: A prefix of
|
||||
// “0b” or “0B” selects base 2, “0”, “0o” or “0O” selects base 8,
|
||||
// and “0x” or “0X” selects base 16. Otherwise, the selected base is 10
|
||||
// and no prefix is accepted.
|
||||
//
|
||||
// For bases <= 36, lower and upper case letters are considered the same:
|
||||
// The letters 'a' to 'z' and 'A' to 'Z' represent digit values 10 to 35.
|
||||
// For bases > 36, the upper case letters 'A' to 'Z' represent the digit
|
||||
// values 36 to 61.
|
||||
//
|
||||
// For base 0, an underscore character “_” may appear between a base
|
||||
// prefix and an adjacent digit, and between successive digits; such
|
||||
// underscores do not change the value of the number.
|
||||
// Incorrect placement of underscores is reported as an error if there
|
||||
// are no other errors. If base != 0, underscores are not recognized
|
||||
// and act like any other character that is not a valid digit.
|
||||
func (z *Int) SetString(s string, base int) (*Int, bool) {
|
||||
}
|
||||
|
||||
// SetBytes interprets buf as the bytes of a big-endian unsigned
|
||||
// integer, sets z to that value, and returns z.
|
||||
func (z *Int) SetBytes(buf []byte) *Int {
|
||||
}
|
||||
|
||||
// Bytes returns the absolute value of x as a big-endian byte slice.
|
||||
//
|
||||
// To use a fixed length slice, or a preallocated one, use FillBytes.
|
||||
func (x *Int) Bytes() []byte {
|
||||
}
|
||||
|
||||
// FillBytes sets buf to the absolute value of x, storing it as a zero-extended
|
||||
// big-endian byte slice, and returns buf.
|
||||
//
|
||||
// If the absolute value of x doesn't fit in buf, FillBytes will panic.
|
||||
func (x *Int) FillBytes(buf []byte) []byte {
|
||||
}
|
||||
|
||||
// BitLen returns the length of the absolute value of x in bits.
|
||||
// The bit length of 0 is 0.
|
||||
func (x *Int) BitLen() int {
|
||||
}
|
||||
|
||||
// TrailingZeroBits returns the number of consecutive least significant zero
|
||||
// bits of |x|.
|
||||
func (x *Int) TrailingZeroBits() uint {
|
||||
}
|
||||
*/
|
||||
|
||||
// Exp sets z = x**y mod |m| (i.e. the sign of m is ignored), and returns z.
|
||||
// If m == nil or m == 0, z = x**y unless y <= 0 then z = 1. If m != 0, y < 0,
|
||||
// and x and m are not relatively prime, z is unchanged and nil is returned.
|
||||
//
|
||||
// Modular exponentiation of inputs of a particular size is not a
|
||||
// cryptographically constant-time operation.
|
||||
func (z *Int) Exp(x, y, m *Int) *Int {
|
||||
ctx := ctxGet()
|
||||
mbn := (*openssl.BIGNUM)(m)
|
||||
if mbn == nil || mbn.IsZero() != 0 {
|
||||
(*openssl.BIGNUM)(z).Exp((*openssl.BIGNUM)(x), (*openssl.BIGNUM)(y), ctx)
|
||||
} else {
|
||||
(*openssl.BIGNUM)(z).ModExp((*openssl.BIGNUM)(x), (*openssl.BIGNUM)(y), mbn, ctx)
|
||||
}
|
||||
ctxPut(ctx)
|
||||
return z
|
||||
}
|
||||
|
||||
/*
|
||||
// GCD sets z to the greatest common divisor of a and b and returns z.
|
||||
// If x or y are not nil, GCD sets their value such that z = a*x + b*y.
|
||||
//
|
||||
// a and b may be positive, zero or negative. (Before Go 1.14 both had
|
||||
// to be > 0.) Regardless of the signs of a and b, z is always >= 0.
|
||||
//
|
||||
// If a == b == 0, GCD sets z = x = y = 0.
|
||||
//
|
||||
// If a == 0 and b != 0, GCD sets z = |b|, x = 0, y = sign(b) * 1.
|
||||
//
|
||||
// If a != 0 and b == 0, GCD sets z = |a|, x = sign(a) * 1, y = 0.
|
||||
func (z *Int) GCD(x, y, a, b *Int) *Int {
|
||||
}
|
||||
|
||||
// Rand sets z to a pseudo-random number in [0, n) and returns z.
|
||||
//
|
||||
// As this uses the math/rand package, it must not be used for
|
||||
// security-sensitive work. Use crypto/rand.Int instead.
|
||||
func (z *Int) Rand(rnd *rand.Rand, n *Int) *Int {
|
||||
}
|
||||
|
||||
// ModInverse sets z to the multiplicative inverse of g in the ring ℤ/nℤ
|
||||
// and returns z. If g and n are not relatively prime, g has no multiplicative
|
||||
// inverse in the ring ℤ/nℤ. In this case, z is unchanged and the return value
|
||||
// is nil. If n == 0, a division-by-zero run-time panic occurs.
|
||||
func (z *Int) ModInverse(g, n *Int) *Int {
|
||||
}
|
||||
|
||||
// Jacobi returns the Jacobi symbol (x/y), either +1, -1, or 0.
|
||||
// The y argument must be an odd integer.
|
||||
func Jacobi(x, y *Int) int {
|
||||
}
|
||||
|
||||
// ModSqrt sets z to a square root of x mod p if such a square root exists, and
|
||||
// returns z. The modulus p must be an odd prime. If x is not a square mod p,
|
||||
// ModSqrt leaves z unchanged and returns nil. This function panics if p is
|
||||
// not an odd integer, its behavior is undefined if p is odd but not prime.
|
||||
func (z *Int) ModSqrt(x, p *Int) *Int {
|
||||
}
|
||||
|
||||
// Lsh sets z = x << n and returns z.
|
||||
func (z *Int) Lsh(x *Int, n uint) *Int {
|
||||
}
|
||||
|
||||
// Rsh sets z = x >> n and returns z.
|
||||
func (z *Int) Rsh(x *Int, n uint) *Int {
|
||||
}
|
||||
|
||||
// Bit returns the value of the i'th bit of x. That is, it
|
||||
// returns (x>>i)&1. The bit index i must be >= 0.
|
||||
func (x *Int) Bit(i int) uint {
|
||||
}
|
||||
|
||||
// SetBit sets z to x, with x's i'th bit set to b (0 or 1).
|
||||
// That is, if b is 1 SetBit sets z = x | (1 << i);
|
||||
// if b is 0 SetBit sets z = x &^ (1 << i). If b is not 0 or 1,
|
||||
// SetBit will panic.
|
||||
func (z *Int) SetBit(x *Int, i int, b uint) *Int {
|
||||
}
|
||||
|
||||
// And sets z = x & y and returns z.
|
||||
func (z *Int) And(x, y *Int) *Int {
|
||||
}
|
||||
|
||||
// AndNot sets z = x &^ y and returns z.
|
||||
func (z *Int) AndNot(x, y *Int) *Int {
|
||||
}
|
||||
|
||||
// Or sets z = x | y and returns z.
|
||||
func (z *Int) Or(x, y *Int) *Int {
|
||||
}
|
||||
|
||||
// Xor sets z = x ^ y and returns z.
|
||||
func (z *Int) Xor(x, y *Int) *Int {
|
||||
}
|
||||
|
||||
// Not sets z = ^x and returns z.
|
||||
func (z *Int) Not(x *Int) *Int {
|
||||
}
|
||||
|
||||
// Sqrt sets z to ⌊√x⌋, the largest integer such that z² ≤ x, and returns z.
|
||||
// It panics if x is negative.
|
||||
func (z *Int) Sqrt(x *Int) *Int {
|
||||
}
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
70
internal/lib/math/big/intconv.go
Normal file
70
internal/lib/math/big/intconv.go
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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 big
|
||||
|
||||
import (
|
||||
"github.com/goplus/llgo/c"
|
||||
"github.com/goplus/llgo/c/openssl"
|
||||
)
|
||||
|
||||
/*
|
||||
// Text returns the string representation of x in the given base.
|
||||
// Base must be between 2 and 62, inclusive. The result uses the
|
||||
// lower-case letters 'a' to 'z' for digit values 10 to 35, and
|
||||
// the upper-case letters 'A' to 'Z' for digit values 36 to 61.
|
||||
// No prefix (such as "0x") is added to the string. If x is a nil
|
||||
// pointer it returns "<nil>".
|
||||
func (x *Int) Text(base int) string {
|
||||
}
|
||||
|
||||
// Append appends the string representation of x, as generated by
|
||||
// x.Text(base), to buf and returns the extended buffer.
|
||||
func (x *Int) Append(buf []byte, base int) []byte {
|
||||
}
|
||||
*/
|
||||
|
||||
// String returns the decimal representation of x as generated by
|
||||
// x.Text(10).
|
||||
func (x *Int) String() string {
|
||||
// TODO(xsw): can optimize it?
|
||||
cstr := (*openssl.BIGNUM)(x).CStr()
|
||||
ret := c.GoString(cstr)
|
||||
openssl.FreeCStr(cstr)
|
||||
return ret
|
||||
}
|
||||
|
||||
/*
|
||||
// Format implements fmt.Formatter. It accepts the formats
|
||||
// 'b' (binary), 'o' (octal with 0 prefix), 'O' (octal with 0o prefix),
|
||||
// 'd' (decimal), 'x' (lowercase hexadecimal), and
|
||||
// 'X' (uppercase hexadecimal).
|
||||
// Also supported are the full suite of package fmt's format
|
||||
// flags for integral types, including '+' and ' ' for sign
|
||||
// control, '#' for leading zero in octal and for hexadecimal,
|
||||
// a leading "0x" or "0X" for "%#x" and "%#X" respectively,
|
||||
// specification of minimum digits precision, output field
|
||||
// width, space or zero padding, and '-' for left or right
|
||||
// justification.
|
||||
func (x *Int) Format(s fmt.State, ch rune) {
|
||||
}
|
||||
|
||||
// Scan is a support routine for fmt.Scanner; it sets z to the value of
|
||||
// the scanned number. It accepts the formats 'b' (binary), 'o' (octal),
|
||||
// 'd' (decimal), 'x' (lowercase hexadecimal), and 'X' (uppercase hexadecimal).
|
||||
func (z *Int) Scan(s fmt.ScanState, ch rune) error {
|
||||
}
|
||||
*/
|
||||
@@ -98,11 +98,11 @@ type Once struct {
|
||||
func (o *Once) Do(f func()) {
|
||||
if !o.done {
|
||||
o.m.Lock()
|
||||
defer o.m.Unlock()
|
||||
if !o.done {
|
||||
o.done = true
|
||||
f()
|
||||
}
|
||||
o.m.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -154,6 +154,9 @@ func (b Builder) abiMethodOf(mPkg *types.Package, mName string, mSig *types.Sign
|
||||
|
||||
func (b Builder) abiMthd(mPkg *types.Package, mName string, mSig *types.Signature, name, abiTyp, ifn llvm.Value) (ret Expr, tfn llvm.Value) {
|
||||
fullName := FuncName(mPkg, mName, mSig.Recv())
|
||||
if b.Pkg.fnlink != nil {
|
||||
fullName = b.Pkg.fnlink(fullName)
|
||||
}
|
||||
tfn = b.Pkg.NewFunc(fullName, mSig, InGo).impl // TODO(xsw): use rawType to speed up
|
||||
if ifn.IsNil() {
|
||||
ifn = tfn
|
||||
|
||||
@@ -50,7 +50,7 @@ func TestFromTestrt(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFromTestdata(t *testing.T) {
|
||||
cltest.FromDir(t, "", "../cl/_testdata", false)
|
||||
cltest.FromDir(t, "", "../cl/_testdata", true)
|
||||
}
|
||||
|
||||
func TestMakeInterface(t *testing.T) {
|
||||
|
||||
690
ssa/coro.go
Normal file
690
ssa/coro.go
Normal file
@@ -0,0 +1,690 @@
|
||||
/*
|
||||
* 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 ssa
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"go/types"
|
||||
)
|
||||
|
||||
// declare void @llvm.coro.destroy(ptr <handle>)
|
||||
// declare void @llvm.coro.resume(ptr <handle>)
|
||||
// declare i1 @llvm.coro.done(ptr <handle>)
|
||||
// declare ptr @llvm.coro.promise(ptr <ptr>, i32 <alignment>, i1 <from>)
|
||||
// declare i32 @llvm.coro.size.i32()
|
||||
// declare i64 @llvm.coro.size.i64()
|
||||
// declare i32 @llvm.coro.align.i32()
|
||||
// declare i64 @llvm.coro.align.i64()
|
||||
// declare ptr @llvm.coro.begin(token <id>, ptr <mem>)
|
||||
// declare ptr @llvm.coro.free(token %id, ptr <frame>)
|
||||
// declare i1 @llvm.coro.alloc(token <id>)
|
||||
// declare ptr @llvm.coro.noop()
|
||||
// declare ptr @llvm.coro.frame()
|
||||
// declare token @llvm.coro.id(i32 <align>, ptr <promise>, ptr <coroaddr>, ptr <fnaddrs>)
|
||||
// declare token @llvm.coro.id.async(i32 <context size>, i32 <align>, ptr <context arg>, ptr <async function pointer>)
|
||||
// declare token @llvm.coro.id.retcon(i32 <size>, i32 <align>, ptr <buffer>, ptr <continuation prototype>, ptr <alloc>, ptr <dealloc>)
|
||||
// declare token @llvm.coro.id.retcon.once(i32 <size>, i32 <align>, ptr <buffer>, ptr <prototype>, ptr <alloc>, ptr <dealloc>)
|
||||
// declare i1 @llvm.coro.end(ptr <handle>, i1 <unwind>, token <result.token>)
|
||||
// declare token @llvm.coro.end.results(...)
|
||||
// declare i1 @llvm.coro.end.async(ptr <handle>, i1 <unwind>, ...)
|
||||
// declare i8 @llvm.coro.suspend(token <save>, i1 <final>)
|
||||
// declare token @llvm.coro.save(ptr <handle>)
|
||||
// declare {ptr, ptr, ptr} @llvm.coro.suspend.async(ptr <resume function>, ptr <context projection function>, ... <function to call> ... <arguments to function>)
|
||||
// declare ptr @llvm.coro.prepare.async(ptr <coroutine function>)
|
||||
// declare i1 @llvm.coro.suspend.retcon(...)
|
||||
// declare void @await_suspend_function(ptr %awaiter, ptr %hdl)
|
||||
// declare void @llvm.coro.await.suspend.void(ptr <awaiter>, ptr <handle>, ptr <await_suspend_function>)
|
||||
// declare i1 @llvm.coro.await.suspend.bool(ptr <awaiter>, ptr <handle>, ptr <await_suspend_function>)
|
||||
// declare void @llvm.coro.await.suspend.handle(ptr <awaiter>, ptr <handle>, ptr <await_suspend_function>)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// declare void @llvm.coro.destroy(ptr <handle>)
|
||||
func (p Program) tyCoDestroy() *types.Signature {
|
||||
if p.coDestroyTy == nil {
|
||||
i8Ptr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type)
|
||||
params := types.NewTuple(i8Ptr)
|
||||
p.coDestroyTy = types.NewSignatureType(nil, nil, nil, params, nil, false)
|
||||
}
|
||||
return p.coDestroyTy
|
||||
}
|
||||
|
||||
// declare void @llvm.coro.resume(ptr <handle>)
|
||||
func (p Program) tyCoResume() *types.Signature {
|
||||
if p.coResumeTy == nil {
|
||||
i8Ptr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type)
|
||||
params := types.NewTuple(i8Ptr)
|
||||
p.coResumeTy = types.NewSignatureType(nil, nil, nil, params, nil, false)
|
||||
}
|
||||
return p.coResumeTy
|
||||
}
|
||||
|
||||
// declare i1 @llvm.coro.done(ptr <handle>)
|
||||
func (p Program) tyCoDone() *types.Signature {
|
||||
if p.coDoneTy == nil {
|
||||
i8Ptr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type)
|
||||
params := types.NewTuple(i8Ptr)
|
||||
results := types.NewTuple(types.NewParam(token.NoPos, nil, "", p.Bool().raw.Type))
|
||||
p.coDoneTy = types.NewSignatureType(nil, nil, nil, params, results, false)
|
||||
}
|
||||
return p.coDoneTy
|
||||
}
|
||||
|
||||
// declare ptr @llvm.coro.promise(ptr <ptr>, i32 <alignment>, i1 <from>)
|
||||
func (p Program) tyCoPromise() *types.Signature {
|
||||
if p.coPromiseTy == nil {
|
||||
i8Ptr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type)
|
||||
i32 := types.NewParam(token.NoPos, nil, "", p.Int32().raw.Type)
|
||||
boolParam := types.NewParam(token.NoPos, nil, "", p.Bool().raw.Type)
|
||||
params := types.NewTuple(i8Ptr, i32, boolParam)
|
||||
results := types.NewTuple(i8Ptr)
|
||||
p.coPromiseTy = types.NewSignatureType(nil, nil, nil, params, results, false)
|
||||
}
|
||||
return p.coPromiseTy
|
||||
}
|
||||
|
||||
// declare i32 @llvm.coro.size.i32()
|
||||
func (p Program) tyCoSizeI32() *types.Signature {
|
||||
if p.coSizeI32Ty == nil {
|
||||
results := types.NewTuple(types.NewParam(token.NoPos, nil, "", p.Int32().raw.Type))
|
||||
p.coSizeI32Ty = types.NewSignatureType(nil, nil, nil, nil, results, false)
|
||||
}
|
||||
return p.coSizeI32Ty
|
||||
}
|
||||
|
||||
// declare i64 @llvm.coro.size.i64()
|
||||
func (p Program) tyCoSizeI64() *types.Signature {
|
||||
if p.coSizeI64Ty == nil {
|
||||
results := types.NewTuple(types.NewParam(token.NoPos, nil, "", p.Int64().raw.Type))
|
||||
p.coSizeI64Ty = types.NewSignatureType(nil, nil, nil, nil, results, false)
|
||||
}
|
||||
return p.coSizeI64Ty
|
||||
}
|
||||
|
||||
// declare i32 @llvm.coro.align.i32()
|
||||
func (p Program) tyCoAlignI32() *types.Signature {
|
||||
if p.coAlignI32Ty == nil {
|
||||
results := types.NewTuple(types.NewParam(token.NoPos, nil, "", p.Int32().raw.Type))
|
||||
p.coAlignI32Ty = types.NewSignatureType(nil, nil, nil, nil, results, false)
|
||||
}
|
||||
return p.coAlignI32Ty
|
||||
}
|
||||
|
||||
// declare i64 @llvm.coro.align.i64()
|
||||
func (p Program) tyCoAlignI64() *types.Signature {
|
||||
if p.coAlignI64Ty == nil {
|
||||
results := types.NewTuple(types.NewParam(token.NoPos, nil, "", p.Int64().raw.Type))
|
||||
p.coAlignI64Ty = types.NewSignatureType(nil, nil, nil, nil, results, false)
|
||||
}
|
||||
return p.coAlignI64Ty
|
||||
}
|
||||
|
||||
// declare ptr @llvm.coro.begin(token <id>, ptr <mem>)
|
||||
func (p Program) tyCoBegin() *types.Signature {
|
||||
if p.coBeginTy == nil {
|
||||
tokenParam := types.NewParam(token.NoPos, nil, "", p.Token().raw.Type)
|
||||
i8Ptr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type)
|
||||
params := types.NewTuple(tokenParam, i8Ptr)
|
||||
results := types.NewTuple(i8Ptr)
|
||||
p.coBeginTy = types.NewSignatureType(nil, nil, nil, params, results, false)
|
||||
}
|
||||
return p.coBeginTy
|
||||
}
|
||||
|
||||
// declare ptr @llvm.coro.free(token %id, ptr <frame>)
|
||||
func (p Program) tyCoFree() *types.Signature {
|
||||
if p.coFreeTy == nil {
|
||||
tokenParam := types.NewParam(token.NoPos, nil, "", p.Token().raw.Type)
|
||||
i8Ptr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type)
|
||||
params := types.NewTuple(tokenParam, i8Ptr)
|
||||
results := types.NewTuple(i8Ptr)
|
||||
p.coFreeTy = types.NewSignatureType(nil, nil, nil, params, results, false)
|
||||
}
|
||||
return p.coFreeTy
|
||||
}
|
||||
|
||||
// declare i1 @llvm.coro.alloc(token <id>)
|
||||
func (p Program) tyCoAlloc() *types.Signature {
|
||||
if p.coAllocTy == nil {
|
||||
tokenParam := types.NewParam(token.NoPos, nil, "", p.Token().raw.Type)
|
||||
params := types.NewTuple(tokenParam)
|
||||
boolParam := types.NewParam(token.NoPos, nil, "", p.Bool().raw.Type)
|
||||
results := types.NewTuple(boolParam)
|
||||
p.coAllocTy = types.NewSignatureType(nil, nil, nil, params, results, false)
|
||||
}
|
||||
return p.coAllocTy
|
||||
}
|
||||
|
||||
// declare ptr @llvm.coro.noop()
|
||||
func (p Program) tyCoNoop() *types.Signature {
|
||||
if p.coNoopTy == nil {
|
||||
results := types.NewTuple(types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type))
|
||||
p.coNoopTy = types.NewSignatureType(nil, nil, nil, nil, results, false)
|
||||
}
|
||||
return p.coNoopTy
|
||||
}
|
||||
|
||||
// declare ptr @llvm.coro.frame()
|
||||
func (p Program) tyCoFrame() *types.Signature {
|
||||
if p.coFrameTy == nil {
|
||||
results := types.NewTuple(types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type))
|
||||
p.coFrameTy = types.NewSignatureType(nil, nil, nil, nil, results, false)
|
||||
}
|
||||
return p.coFrameTy
|
||||
}
|
||||
|
||||
// declare token @llvm.coro.id(i32 <align>, ptr <promise>, ptr <coroaddr>, ptr <fnaddrs>)
|
||||
func (p Program) tyCoID() *types.Signature {
|
||||
if p.coIDTy == nil {
|
||||
i32 := types.NewParam(token.NoPos, nil, "", p.Int32().raw.Type)
|
||||
i8Ptr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type)
|
||||
params := types.NewTuple(i32, i8Ptr, i8Ptr, i8Ptr)
|
||||
tokenParam := types.NewParam(token.NoPos, nil, "", p.Token().raw.Type)
|
||||
results := types.NewTuple(tokenParam)
|
||||
p.coIDTy = types.NewSignatureType(nil, nil, nil, params, results, false)
|
||||
}
|
||||
return p.coIDTy
|
||||
}
|
||||
|
||||
/*
|
||||
// declare token @llvm.coro.id.async(i32 <context size>, i32 <align>, ptr <context arg>, ptr <async function pointer>)
|
||||
func (p Program) tyCoIDAsync() *types.Signature {
|
||||
if p.coIDAsyncTy == nil {
|
||||
i32 := types.NewParam(token.NoPos, nil, "", p.Int32().raw.Type)
|
||||
i8Ptr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type)
|
||||
params := types.NewTuple(i32, i32, i8Ptr, i8Ptr)
|
||||
tokenParam := types.NewParam(token.NoPos, nil, "", p.Token().raw.Type)
|
||||
results := types.NewTuple(tokenParam)
|
||||
p.coIDAsyncTy = types.NewSignatureType(nil, nil, nil, params, results, false)
|
||||
}
|
||||
return p.coIDAsyncTy
|
||||
}
|
||||
|
||||
// declare token @llvm.coro.id.retcon(i32 <size>, i32 <align>, ptr <buffer>, ptr <continuation prototype>, ptr <alloc>, ptr <dealloc>)
|
||||
func (p Program) tyCoIDRetcon() *types.Signature {
|
||||
if p.coIDRetconTy == nil {
|
||||
i32 := types.NewParam(token.NoPos, nil, "", p.Int32().raw.Type)
|
||||
i8Ptr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type)
|
||||
params := types.NewTuple(i32, i32, i8Ptr, i8Ptr, i8Ptr, i8Ptr)
|
||||
tokenParam := types.NewParam(token.NoPos, nil, "", p.Token().raw.Type)
|
||||
results := types.NewTuple(tokenParam)
|
||||
p.coIDRetconTy = types.NewSignatureType(nil, nil, nil, params, results, false)
|
||||
}
|
||||
return p.coIDRetconTy
|
||||
}
|
||||
|
||||
// declare token @llvm.coro.id.retcon.once(i32 <size>, i32 <align>, ptr <buffer>, ptr <prototype>, ptr <alloc>, ptr <dealloc>)
|
||||
func (p Program) tyCoIDRetconOnce() *types.Signature {
|
||||
if p.coIDRetconOnceTy == nil {
|
||||
i32 := types.NewParam(token.NoPos, nil, "", p.Int32().raw.Type)
|
||||
i8Ptr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type)
|
||||
params := types.NewTuple(i32, i32, i8Ptr, i8Ptr, i8Ptr, i8Ptr)
|
||||
tokenParam := types.NewParam(token.NoPos, nil, "", p.Token().raw.Type)
|
||||
results := types.NewTuple(tokenParam)
|
||||
p.coIDRetconOnceTy = types.NewSignatureType(nil, nil, nil, params, results, false)
|
||||
}
|
||||
return p.coIDRetconOnceTy
|
||||
}
|
||||
*/
|
||||
|
||||
// declare i1 @llvm.coro.end(ptr <handle>, i1 <unwind>, token <result.token>)
|
||||
func (p Program) tyCoEnd() *types.Signature {
|
||||
if p.coEndTy == nil {
|
||||
i8Ptr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type)
|
||||
boolParam := types.NewParam(token.NoPos, nil, "", p.Bool().raw.Type)
|
||||
tokenParam := types.NewParam(token.NoPos, nil, "", p.Token().raw.Type)
|
||||
params := types.NewTuple(i8Ptr, boolParam, tokenParam)
|
||||
results := types.NewTuple(boolParam)
|
||||
p.coEndTy = types.NewSignatureType(nil, nil, nil, params, results, false)
|
||||
}
|
||||
return p.coEndTy
|
||||
}
|
||||
|
||||
/*
|
||||
// TODO(lijie): varargs
|
||||
// declare token @llvm.coro.end.results(...)
|
||||
func (p Program) tyCoEndResults() *types.Signature {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// TODO(lijie): varargs
|
||||
// declare i1 @llvm.coro.end.async(ptr <handle>, i1 <unwind>, ...)
|
||||
func (p Program) tyCoEndAsync() *types.Signature {
|
||||
panic("not implemented")
|
||||
}
|
||||
*/
|
||||
|
||||
// declare i8 @llvm.coro.suspend(token <save>, i1 <final>)
|
||||
func (p Program) tyCoSuspend() *types.Signature {
|
||||
if p.coSuspendTy == nil {
|
||||
tokenParam := types.NewParam(token.NoPos, nil, "", p.Token().raw.Type)
|
||||
boolParam := types.NewParam(token.NoPos, nil, "", p.Bool().raw.Type)
|
||||
params := types.NewTuple(tokenParam, boolParam)
|
||||
paramByte := types.NewParam(token.NoPos, nil, "", p.Byte().raw.Type)
|
||||
results := types.NewTuple(paramByte)
|
||||
p.coSuspendTy = types.NewSignatureType(nil, nil, nil, params, results, false)
|
||||
}
|
||||
return p.coSuspendTy
|
||||
}
|
||||
|
||||
/*
|
||||
// declare token @llvm.coro.save(ptr <handle>)
|
||||
func (p Program) tyCoSave() *types.Signature {
|
||||
if p.coSaveTy == nil {
|
||||
i8Ptr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type)
|
||||
params := types.NewTuple(i8Ptr)
|
||||
tokenParam := types.NewParam(token.NoPos, nil, "", p.Token().raw.Type)
|
||||
results := types.NewTuple(tokenParam)
|
||||
p.coSaveTy = types.NewSignatureType(nil, nil, nil, params, results, false)
|
||||
}
|
||||
return p.coSaveTy
|
||||
}
|
||||
|
||||
// TODO(lijie): varargs
|
||||
// declare {ptr, ptr, ptr} @llvm.coro.suspend.async(ptr <resume function>, ptr <context projection function>, ... <function to call> ... <arguments to function>)
|
||||
func (p Program) tyCoSuspendAsync() *types.Signature {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// declare ptr @llvm.coro.prepare.async(ptr <coroutine function>)
|
||||
func (p Program) tyCoPrepareAsync() *types.Signature {
|
||||
if p.coPrepareAsyncTy == nil {
|
||||
i8Ptr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type)
|
||||
params := types.NewTuple(i8Ptr)
|
||||
results := types.NewTuple(i8Ptr)
|
||||
p.coPrepareAsyncTy = types.NewSignatureType(nil, nil, nil, params, results, false)
|
||||
}
|
||||
return p.coPrepareAsyncTy
|
||||
}
|
||||
|
||||
// declare i1 @llvm.coro.suspend.retcon(...)
|
||||
func (p Program) tyCoSuspendRetcon() *types.Signature {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// declare void @await_suspend_function(ptr %awaiter, ptr %hdl)
|
||||
func (p Program) tyCoAwaitSuspendFunction() *types.Signature {
|
||||
if p.coAwaitSuspendFunctionTy == nil {
|
||||
i8Ptr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type)
|
||||
params := types.NewTuple(i8Ptr, i8Ptr)
|
||||
p.coAwaitSuspendFunctionTy = types.NewSignatureType(nil, nil, nil, params, nil, false)
|
||||
}
|
||||
return p.coAwaitSuspendFunctionTy
|
||||
}
|
||||
|
||||
// declare void @llvm.coro.await.suspend.void(ptr <awaiter>, ptr <handle>, ptr <await_suspend_function>)
|
||||
func (p Program) tyCoAwaitSuspendVoid() *types.Signature {
|
||||
if p.coAwaitSuspendVoidTy == nil {
|
||||
i8Ptr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type)
|
||||
params := types.NewTuple(i8Ptr, i8Ptr, i8Ptr)
|
||||
p.coAwaitSuspendVoidTy = types.NewSignatureType(nil, nil, nil, params, nil, false)
|
||||
}
|
||||
return p.coAwaitSuspendVoidTy
|
||||
}
|
||||
|
||||
// declare i1 @llvm.coro.await.suspend.bool(ptr <awaiter>, ptr <handle>, ptr <await_suspend_function>)
|
||||
func (p Program) tyCoAwaitSuspendBool() *types.Signature {
|
||||
if p.coAwaitSuspendBoolTy == nil {
|
||||
i8Ptr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type)
|
||||
params := types.NewTuple(i8Ptr, i8Ptr, i8Ptr)
|
||||
results := types.NewTuple(types.NewParam(token.NoPos, nil, "", p.Bool().raw.Type))
|
||||
p.coAwaitSuspendBoolTy = types.NewSignatureType(nil, nil, nil, params, results, false)
|
||||
}
|
||||
return p.coAwaitSuspendBoolTy
|
||||
}
|
||||
|
||||
// declare void @llvm.coro.await.suspend.handle(ptr <awaiter>, ptr <handle>, ptr <await_suspend_function>)
|
||||
func (p Program) tyCoAwaitSuspendHandle() *types.Signature {
|
||||
if p.coAwaitSuspendHandleTy == nil {
|
||||
i8Ptr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type)
|
||||
params := types.NewTuple(i8Ptr, i8Ptr, i8Ptr)
|
||||
p.coAwaitSuspendHandleTy = types.NewSignatureType(nil, nil, nil, params, nil, false)
|
||||
}
|
||||
return p.coAwaitSuspendHandleTy
|
||||
}
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
func (b Builder) SetBlockOffset(offset int) {
|
||||
b.blkOffset = offset
|
||||
}
|
||||
|
||||
func (b Builder) BlockOffset() int {
|
||||
return b.blkOffset
|
||||
}
|
||||
|
||||
func (b Builder) Async() bool {
|
||||
return b.async
|
||||
}
|
||||
|
||||
func (b Builder) AsyncToken() Expr {
|
||||
return b.asyncToken
|
||||
}
|
||||
|
||||
func (b Builder) EndAsync() {
|
||||
b.onReturn()
|
||||
}
|
||||
|
||||
/*
|
||||
pesudo code:
|
||||
|
||||
retPtr := malloc(sizeof(Promise))
|
||||
id := @llvm.coro.id(0, null, null, null)
|
||||
promiseSize := sizeof(Promise[T])
|
||||
frameSize := @llvm.coro.size.i64()
|
||||
allocSize := promiseSize + frameSize
|
||||
promise := malloc(allocSize)
|
||||
needAlloc := @llvm.coro.alloc(id)
|
||||
; Allocate memory for return type and coroutine frame
|
||||
frame := null
|
||||
|
||||
if needAlloc {
|
||||
frame := malloc(frameSize)
|
||||
}
|
||||
|
||||
hdl := @llvm.coro.begin(id, frame)
|
||||
*retPtr = hdl
|
||||
*/
|
||||
func (b Builder) BeginAsync(fn Function, entryBlk, allocBlk, cleanBlk, suspdBlk, trapBlk, beginBlk BasicBlock) {
|
||||
ty := fn.Type.RawType().(*types.Signature).Results().At(0).Type()
|
||||
ptrTy, ok := ty.(*types.Pointer)
|
||||
if !ok {
|
||||
panic("async function must return a *async.Promise")
|
||||
}
|
||||
promiseTy := b.Prog.Type(ptrTy.Elem(), InGo)
|
||||
|
||||
b.async = true
|
||||
|
||||
b.SetBlock(entryBlk)
|
||||
align := b.Const(constant.MakeInt64(0), b.Prog.CInt()).SetName("align")
|
||||
null := b.Const(nil, b.Prog.CIntPtr())
|
||||
id := b.CoID(align, null, null, null).SetName("id")
|
||||
b.asyncToken = id
|
||||
promiseSize := b.Const(constant.MakeUint64(b.Prog.SizeOf(promiseTy)), b.Prog.Int64()).SetName("alloc.size")
|
||||
frameSize := b.CoSizeI64().SetName("frame.size")
|
||||
allocSize := b.BinOp(token.ADD, promiseSize, frameSize).SetName("alloc.size")
|
||||
promise := b.AllocZ(allocSize).SetName("promise")
|
||||
b.promise = promise
|
||||
promise.Type = b.Prog.Pointer(promiseTy)
|
||||
needAlloc := b.CoAlloc(id).SetName("need.dyn.alloc")
|
||||
b.If(needAlloc, allocBlk, beginBlk)
|
||||
|
||||
b.SetBlock(allocBlk)
|
||||
frame := b.OffsetPtr(promise, promiseSize)
|
||||
b.Jump(beginBlk)
|
||||
|
||||
b.SetBlock(beginBlk)
|
||||
phi := b.Phi(b.Prog.VoidPtr())
|
||||
phi.SetName("frame")
|
||||
phi.AddIncoming(b, []BasicBlock{entryBlk, allocBlk}, func(i int, blk BasicBlock) Expr {
|
||||
if i == 0 {
|
||||
return null
|
||||
}
|
||||
return frame
|
||||
})
|
||||
hdl := b.CoBegin(id, phi.Expr)
|
||||
hdl.SetName("hdl")
|
||||
b.Store(promise, hdl)
|
||||
|
||||
b.SetBlock(cleanBlk)
|
||||
b.CoFree(id, hdl)
|
||||
b.Jump(suspdBlk)
|
||||
|
||||
b.SetBlock(suspdBlk)
|
||||
b.CoEnd(hdl, b.Prog.BoolVal(false), b.Prog.TokenNone())
|
||||
b.Return(promise)
|
||||
|
||||
b.SetBlock(trapBlk)
|
||||
b.LLVMTrap()
|
||||
b.Unreachable()
|
||||
|
||||
b.onSuspBlk = func(nextBlk BasicBlock) (BasicBlock, BasicBlock, BasicBlock) {
|
||||
if nextBlk == nil {
|
||||
nextBlk = trapBlk
|
||||
}
|
||||
return suspdBlk, nextBlk, cleanBlk
|
||||
}
|
||||
b.onReturn = func() {
|
||||
b.CoSuspend(b.asyncToken, b.Prog.BoolVal(true), trapBlk)
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// declare void @llvm.coro.destroy(ptr <handle>)
|
||||
func (b Builder) CoDestroy(hdl Expr) {
|
||||
fn := b.Pkg.cFunc("llvm.coro.destroy", b.Prog.tyCoDestroy())
|
||||
b.Call(fn, hdl)
|
||||
}
|
||||
|
||||
// declare void @llvm.coro.resume(ptr <handle>)
|
||||
func (b Builder) CoResume(hdl Expr) {
|
||||
fn := b.Pkg.cFunc("llvm.coro.resume", b.Prog.tyCoResume())
|
||||
b.Call(fn, hdl)
|
||||
}
|
||||
|
||||
// declare i1 @llvm.coro.done(ptr <handle>)
|
||||
func (b Builder) coDone(hdl Expr) Expr {
|
||||
fn := b.Pkg.cFunc("llvm.coro.done", b.Prog.tyCoDone())
|
||||
return b.Call(fn, hdl)
|
||||
}
|
||||
|
||||
// return c.Char
|
||||
func (b Builder) CoDone(hdl Expr) Expr {
|
||||
bvar := b.coDone(hdl)
|
||||
// TODO(lijie): inefficient
|
||||
// %6 = zext i1 %5 to i64
|
||||
// %7 = trunc i64 %6 to i8
|
||||
return b.valFromData(b.Prog.Byte(), bvar.impl)
|
||||
}
|
||||
|
||||
// declare ptr @llvm.coro.promise(ptr <ptr>, i32 <alignment>, i1 <from>)
|
||||
func (b Builder) CoPromise(ptr, align, from Expr) Expr {
|
||||
fn := b.Pkg.cFunc("llvm.coro.promise", b.Prog.tyCoPromise())
|
||||
return b.Call(fn, ptr, align, from)
|
||||
}
|
||||
|
||||
// declare i32 @llvm.coro.size.i32()
|
||||
func (b Builder) CoSizeI32() Expr {
|
||||
fn := b.Pkg.cFunc("llvm.coro.size.i32", b.Prog.tyCoSizeI32())
|
||||
return b.Call(fn)
|
||||
}
|
||||
|
||||
// declare i64 @llvm.coro.size.i64()
|
||||
func (b Builder) CoSizeI64() Expr {
|
||||
fn := b.Pkg.cFunc("llvm.coro.size.i64", b.Prog.tyCoSizeI64())
|
||||
return b.Call(fn)
|
||||
}
|
||||
|
||||
// declare i32 @llvm.coro.align.i32()
|
||||
func (b Builder) CoAlignI32() Expr {
|
||||
fn := b.Pkg.cFunc("llvm.coro.align.i32", b.Prog.tyCoAlignI32())
|
||||
return b.Call(fn)
|
||||
}
|
||||
|
||||
// declare i64 @llvm.coro.align.i64()
|
||||
func (b Builder) CoAlignI64() Expr {
|
||||
fn := b.Pkg.cFunc("llvm.coro.align.i64", b.Prog.tyCoAlignI64())
|
||||
return b.Call(fn)
|
||||
}
|
||||
|
||||
// declare ptr @llvm.coro.begin(token <id>, ptr <mem>)
|
||||
func (b Builder) CoBegin(id, mem Expr) Expr {
|
||||
fn := b.Pkg.cFunc("llvm.coro.begin", b.Prog.tyCoBegin())
|
||||
return b.Call(fn, id, mem)
|
||||
}
|
||||
|
||||
// declare ptr @llvm.coro.free(token %id, ptr <frame>)
|
||||
func (b Builder) CoFree(id, frame Expr) Expr {
|
||||
fn := b.Pkg.cFunc("llvm.coro.free", b.Prog.tyCoFree())
|
||||
return b.Call(fn, id, frame)
|
||||
}
|
||||
|
||||
// declare i1 @llvm.coro.alloc(token <id>)
|
||||
func (b Builder) CoAlloc(id Expr) Expr {
|
||||
fn := b.Pkg.cFunc("llvm.coro.alloc", b.Prog.tyCoAlloc())
|
||||
return b.Call(fn, id)
|
||||
}
|
||||
|
||||
// declare ptr @llvm.coro.noop()
|
||||
func (b Builder) CoNoop() Expr {
|
||||
fn := b.Pkg.cFunc("llvm.coro.noop", b.Prog.tyCoNoop())
|
||||
return b.Call(fn)
|
||||
}
|
||||
|
||||
// declare ptr @llvm.coro.frame()
|
||||
func (b Builder) CoFrame() Expr {
|
||||
fn := b.Pkg.cFunc("llvm.coro.frame", b.Prog.tyCoFrame())
|
||||
return b.Call(fn)
|
||||
}
|
||||
|
||||
// declare token @llvm.coro.id(i32 <align>, ptr <promise>, ptr <coroaddr>, ptr <fnaddrs>)
|
||||
func (b Builder) CoID(align Expr, promise, coroAddr, fnAddrs Expr) Expr {
|
||||
if align.Type != b.Prog.Int32() {
|
||||
panic("align must be i32")
|
||||
}
|
||||
fn := b.Pkg.cFunc("llvm.coro.id", b.Prog.tyCoID())
|
||||
return b.Call(fn, align, promise, coroAddr, fnAddrs)
|
||||
}
|
||||
|
||||
/*
|
||||
// declare token @llvm.coro.id.async(i32 <context size>, i32 <align>, ptr <context arg>, ptr <async function pointer>)
|
||||
func (b Builder) CoIDAsync(contextSize, align, contextArg, asyncFnPtr Expr) Expr {
|
||||
if contextSize.Type != b.Prog.Int32() {
|
||||
panic("contextSize must be i32")
|
||||
}
|
||||
if align.Type != b.Prog.Int32() {
|
||||
panic("align must be i32")
|
||||
}
|
||||
fn := b.Pkg.cFunc("llvm.coro.id.async", b.Prog.tyCoIDAsync())
|
||||
return b.Call(fn, contextSize, align, contextArg, asyncFnPtr)
|
||||
}
|
||||
|
||||
// declare token @llvm.coro.id.retcon(i32 <size>, i32 <align>, ptr <buffer>, ptr <continuation prototype>, ptr <alloc>, ptr <dealloc>)
|
||||
func (b Builder) CoIDRetcon(size, align, buffer, contProto, alloc, dealloc Expr) Expr {
|
||||
fn := b.Pkg.cFunc("llvm.coro.id.retcon", b.Prog.tyCoIDRetcon())
|
||||
return b.Call(fn, size, align, buffer, contProto, alloc, dealloc)
|
||||
}
|
||||
|
||||
// declare token @llvm.coro.id.retcon.once(i32 <size>, i32 <align>, ptr <buffer>, ptr <prototype>, ptr <alloc>, ptr <dealloc>)
|
||||
func (b Builder) CoIDRetconOnce(size, align, buffer, prototype, alloc, dealloc Expr) Expr {
|
||||
fn := b.Pkg.cFunc("llvm.coro.id.retcon.once", b.Prog.tyCoIDRetconOnce())
|
||||
return b.Call(fn, size, align, buffer, prototype, alloc, dealloc)
|
||||
}
|
||||
*/
|
||||
|
||||
// declare i1 @llvm.coro.end(ptr <handle>, i1 <unwind>, token <result.token>)
|
||||
func (b Builder) CoEnd(hdl Expr, unwind Expr, resultToken Expr) Expr {
|
||||
fn := b.Pkg.cFunc("llvm.coro.end", b.Prog.tyCoEnd())
|
||||
return b.Call(fn, hdl, unwind, resultToken)
|
||||
}
|
||||
|
||||
/*
|
||||
// declare token @llvm.coro.end.results(...)
|
||||
func (b Builder) CoEndResults(args []Expr) Expr {
|
||||
fn := b.Pkg.cFunc("llvm.coro.end.results", b.Prog.tyCoEndResults())
|
||||
return b.Call(fn, args...)
|
||||
}
|
||||
|
||||
// declare i1 @llvm.coro.end.async(ptr <handle>, i1 <unwind>, ...)
|
||||
func (b Builder) CoEndAsync(handle, unwind Expr, args ...Expr) Expr {
|
||||
fn := b.Pkg.cFunc("llvm.coro.end.async", b.Prog.tyCoEndAsync())
|
||||
vargs := append([]Expr{handle, unwind}, args...)
|
||||
return b.Call(fn, vargs...)
|
||||
}
|
||||
*/
|
||||
|
||||
// declare i8 @llvm.coro.suspend(token <save>, i1 <final>)
|
||||
func (b Builder) coSuspend(save, final Expr) Expr {
|
||||
fn := b.Pkg.cFunc("llvm.coro.suspend", b.Prog.tyCoSuspend())
|
||||
return b.Call(fn, save, final)
|
||||
}
|
||||
|
||||
func (b Builder) CoSuspend(save, final Expr, nextBlk BasicBlock) {
|
||||
if !b.async {
|
||||
panic(fmt.Errorf("suspend %v not in async block", b.Func.Name()))
|
||||
}
|
||||
if nextBlk == nil {
|
||||
b.Func.MakeBlock("")
|
||||
nextBlk = b.Func.Block(b.blk.idx + 1)
|
||||
}
|
||||
ret := b.coSuspend(save, final)
|
||||
susp, next, clean := b.onSuspBlk(nextBlk)
|
||||
swt := b.Switch(ret, susp)
|
||||
swt.Case(b.Const(constant.MakeInt64(0), b.Prog.Byte()), next)
|
||||
swt.Case(b.Const(constant.MakeInt64(1), b.Prog.Byte()), clean)
|
||||
swt.End(b)
|
||||
b.SetBlock(nextBlk)
|
||||
}
|
||||
|
||||
func (b Builder) CoReturn(setValueFn Function, value Expr) {
|
||||
if !b.async {
|
||||
panic(fmt.Errorf("return %v not in async block", b.Func.Name()))
|
||||
}
|
||||
b.Call(setValueFn.Expr, b.promise, value)
|
||||
_, _, cleanBlk := b.onSuspBlk(nil)
|
||||
b.Jump(cleanBlk)
|
||||
}
|
||||
|
||||
func (b Builder) CoYield(setValueFn Function, value Expr, final Expr) {
|
||||
if !b.async {
|
||||
panic(fmt.Errorf("yield %v not in async block", b.Func.Name()))
|
||||
}
|
||||
b.Call(setValueFn.Expr, b.promise, value)
|
||||
b.CoSuspend(b.AsyncToken(), final, nil)
|
||||
}
|
||||
|
||||
/*
|
||||
// declare token @llvm.coro.save(ptr <handle>)
|
||||
func (b Builder) CoSave(hdl Expr) Expr {
|
||||
fn := b.Pkg.cFunc("llvm.coro.save", b.Prog.tyCoSave())
|
||||
return b.Call(fn, hdl)
|
||||
}
|
||||
|
||||
// declare ptr @llvm.coro.prepare.async(ptr <coroutine function>)
|
||||
func (b Builder) CoPrepareAsync(f Expr) Expr {
|
||||
fn := b.Pkg.cFunc("llvm.coro.prepare.async", b.Prog.tyCoPrepareAsync())
|
||||
return b.Call(fn, f)
|
||||
}
|
||||
|
||||
// declare i1 @llvm.coro.suspend.retcon(...)
|
||||
func (b Builder) CoSuspendRetcon(args []Expr) Expr {
|
||||
fn := b.Pkg.cFunc("llvm.coro.suspend.retcon", b.Prog.tyCoSuspendRetcon())
|
||||
return b.Call(fn, args...)
|
||||
}
|
||||
|
||||
// declare void @llvm.coro.await.suspend.void(ptr <awaiter>, ptr <handle>, ptr <await_suspend_function>)
|
||||
func (b Builder) CoAwaitSuspendVoid(awaiter, handle, f Expr) {
|
||||
fn := b.Pkg.cFunc("llvm.coro.await.suspend.void", b.Prog.tyCoAwaitSuspendVoid())
|
||||
b.Call(fn, awaiter, handle, f)
|
||||
}
|
||||
|
||||
// declare i1 @llvm.coro.await.suspend.bool(ptr <awaiter>, ptr <handle>, ptr <await_suspend_function>)
|
||||
func (b Builder) CoAwaitSuspendBool(awaiter, handle, f Expr) Expr {
|
||||
fn := b.Pkg.cFunc("llvm.coro.await.suspend.bool", b.Prog.tyCoAwaitSuspendBool())
|
||||
return b.Call(fn, awaiter, handle, f)
|
||||
}
|
||||
|
||||
// declare void @llvm.coro.await.suspend.handle(ptr <awaiter>, ptr <handle>, ptr <await_suspend_function>)
|
||||
func (b Builder) CoAwaitSuspendHandle(awaiter, handle, f Expr) {
|
||||
fn := b.Pkg.cFunc("llvm.coro.await.suspend.handle", b.Prog.tyCoAwaitSuspendHandle())
|
||||
b.Call(fn, awaiter, handle, f)
|
||||
}
|
||||
*/
|
||||
21
ssa/decl.go
21
ssa/decl.go
@@ -180,11 +180,11 @@ type Function = *aFunction
|
||||
|
||||
// NewFunc creates a new function.
|
||||
func (p Package) NewFunc(name string, sig *types.Signature, bg Background) Function {
|
||||
return p.NewFuncEx(name, sig, bg, false)
|
||||
return p.NewFuncEx(name, sig, bg, false, false)
|
||||
}
|
||||
|
||||
// NewFuncEx creates a new function.
|
||||
func (p Package) NewFuncEx(name string, sig *types.Signature, bg Background, hasFreeVars bool) Function {
|
||||
func (p Package) NewFuncEx(name string, sig *types.Signature, bg Background, hasFreeVars, async bool) Function {
|
||||
if v, ok := p.fns[name]; ok {
|
||||
return v
|
||||
}
|
||||
@@ -193,6 +193,9 @@ func (p Package) NewFuncEx(name string, sig *types.Signature, bg Background, has
|
||||
log.Println("NewFunc", name, t.raw.Type, "hasFreeVars:", hasFreeVars)
|
||||
}
|
||||
fn := llvm.AddFunction(p.mod, name, t.ll)
|
||||
if async {
|
||||
fn.AddFunctionAttr(p.Prog.ctx.CreateStringAttribute("presplitcoroutine", ""))
|
||||
}
|
||||
ret := newFunction(fn, t, p, p.Prog, hasFreeVars)
|
||||
p.fns[name] = ret
|
||||
return ret
|
||||
@@ -268,7 +271,7 @@ func (p Function) NewBuilder() Builder {
|
||||
b := prog.ctx.NewBuilder()
|
||||
// TODO(xsw): Finalize may cause panic, so comment it.
|
||||
// b.Finalize()
|
||||
return &aBuilder{b, nil, p, p.Pkg, prog}
|
||||
return &aBuilder{impl: b, blk: nil, Func: p, Pkg: p.Pkg, Prog: prog}
|
||||
}
|
||||
|
||||
// HasBody reports whether the function has a body.
|
||||
@@ -293,13 +296,15 @@ func (p Function) MakeBlocks(nblk int) []BasicBlock {
|
||||
p.blks = make([]BasicBlock, 0, nblk)
|
||||
}
|
||||
for i := 0; i < nblk; i++ {
|
||||
p.addBlock(n + i)
|
||||
p.addBlock(n+i, "")
|
||||
}
|
||||
return p.blks[n:]
|
||||
}
|
||||
|
||||
func (p Function) addBlock(idx int) BasicBlock {
|
||||
label := "_llgo_" + strconv.Itoa(idx)
|
||||
func (p Function) addBlock(idx int, label string) BasicBlock {
|
||||
if label == "" {
|
||||
label = "_llgo_" + strconv.Itoa(idx)
|
||||
}
|
||||
blk := llvm.AddBasicBlock(p.impl, label)
|
||||
ret := &aBasicBlock{blk, blk, p, idx}
|
||||
p.blks = append(p.blks, ret)
|
||||
@@ -307,8 +312,8 @@ func (p Function) addBlock(idx int) BasicBlock {
|
||||
}
|
||||
|
||||
// MakeBlock creates a new basic block for the function.
|
||||
func (p Function) MakeBlock() BasicBlock {
|
||||
return p.addBlock(len(p.blks))
|
||||
func (p Function) MakeBlock(label string) BasicBlock {
|
||||
return p.addBlock(len(p.blks), label)
|
||||
}
|
||||
|
||||
// Block returns the ith basic block of the function.
|
||||
|
||||
18
ssa/eh.go
18
ssa/eh.go
@@ -55,6 +55,13 @@ func (p Program) tySiglongjmp() *types.Signature {
|
||||
return p.sigljmpTy
|
||||
}
|
||||
|
||||
func (p Program) tyLLVMTrap() *types.Signature {
|
||||
if p.llvmTrapTy == nil {
|
||||
p.llvmTrapTy = types.NewSignatureType(nil, nil, nil, nil, nil, false)
|
||||
}
|
||||
return p.llvmTrapTy
|
||||
}
|
||||
|
||||
func (b Builder) AllocaSigjmpBuf() Expr {
|
||||
prog := b.Prog
|
||||
n := unsafe.Sizeof(sigjmpbuf{})
|
||||
@@ -77,6 +84,11 @@ func (b Builder) Siglongjmp(jb, retval Expr) {
|
||||
// b.Unreachable()
|
||||
}
|
||||
|
||||
func (b Builder) LLVMTrap() {
|
||||
fn := b.Pkg.cFunc("llvm.trap", b.Prog.tyLLVMTrap())
|
||||
b.Call(fn)
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const (
|
||||
@@ -166,7 +178,7 @@ func (b Builder) getDefer(kind DoAction) *aDefer {
|
||||
czero := prog.IntVal(0, prog.CInt())
|
||||
retval := b.Sigsetjmp(jb, czero)
|
||||
if kind != DeferAlways {
|
||||
panicBlk = self.MakeBlock()
|
||||
panicBlk = self.MakeBlock("")
|
||||
} else {
|
||||
blks = self.MakeBlocks(2)
|
||||
next, panicBlk = blks[0], blks[1]
|
||||
@@ -221,7 +233,7 @@ func (b Builder) Defer(kind DoAction, fn Expr, args ...Expr) {
|
||||
case DeferAlways:
|
||||
// nothing to do
|
||||
default:
|
||||
panic("todo: DeferInLoop is not supported")
|
||||
panic("todo: DeferInLoop is not supported - " + b.Func.Name())
|
||||
}
|
||||
self.stmts = append(self.stmts, func(bits Expr) {
|
||||
switch kind {
|
||||
@@ -240,7 +252,7 @@ func (b Builder) Defer(kind DoAction, fn Expr, args ...Expr) {
|
||||
// RunDefers emits instructions to run deferred instructions.
|
||||
func (b Builder) RunDefers() {
|
||||
self := b.getDefer(DeferInCond)
|
||||
blk := b.Func.MakeBlock()
|
||||
blk := b.Func.MakeBlock("")
|
||||
self.rundsNext = append(self.rundsNext, blk)
|
||||
|
||||
b.Store(self.rundPtr, blk.Addr())
|
||||
|
||||
@@ -59,6 +59,11 @@ func (v Expr) SetOrdering(ordering AtomicOrdering) Expr {
|
||||
return v
|
||||
}
|
||||
|
||||
func (v Expr) SetName(name string) Expr {
|
||||
v.impl.SetName(name)
|
||||
return v
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type builtinTy struct {
|
||||
|
||||
@@ -232,6 +232,13 @@ func (b Builder) ArrayAlloca(telem Type, n Expr) (ret Expr) {
|
||||
return
|
||||
}
|
||||
|
||||
func (b Builder) OffsetPtr(ptr, offset Expr) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("OffsetPtr %v, %v\n", ptr.impl, offset.impl)
|
||||
}
|
||||
return Expr{llvm.CreateGEP(b.impl, ptr.Type.ll, ptr.impl, []llvm.Value{offset.impl}), ptr.Type}
|
||||
}
|
||||
|
||||
/* TODO(xsw):
|
||||
// ArrayAlloc allocates zero initialized space for an array of n elements of type telem.
|
||||
func (b Builder) ArrayAlloc(telem Type, n Expr) (ret Expr) {
|
||||
|
||||
@@ -19,8 +19,10 @@ package ssa
|
||||
import (
|
||||
"go/token"
|
||||
"go/types"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goplus/llgo/ssa/abi"
|
||||
@@ -136,6 +138,8 @@ type aProgram struct {
|
||||
rtMapTy llvm.Type
|
||||
rtChanTy llvm.Type
|
||||
|
||||
tokenType llvm.Type
|
||||
|
||||
anyTy Type
|
||||
voidTy Type
|
||||
voidPtr Type
|
||||
@@ -166,6 +170,8 @@ type aProgram struct {
|
||||
deferTy Type
|
||||
deferPtr Type
|
||||
|
||||
tokenTy Type
|
||||
|
||||
pyImpTy *types.Signature
|
||||
pyNewList *types.Signature
|
||||
pyListSetI *types.Signature
|
||||
@@ -189,6 +195,41 @@ type aProgram struct {
|
||||
sigsetjmpTy *types.Signature
|
||||
sigljmpTy *types.Signature
|
||||
|
||||
llvmTrapTy *types.Signature
|
||||
|
||||
// coroutine manipulation intrinsics (ordered by LLVM coroutine doc)
|
||||
coDestroyTy *types.Signature
|
||||
coResumeTy *types.Signature
|
||||
coDoneTy *types.Signature
|
||||
coPromiseTy *types.Signature
|
||||
|
||||
// coroutine structure intrinsics (ordered by LLVM coroutine doc)
|
||||
coSizeI32Ty *types.Signature
|
||||
coSizeI64Ty *types.Signature
|
||||
coAlignI32Ty *types.Signature
|
||||
coAlignI64Ty *types.Signature
|
||||
coBeginTy *types.Signature
|
||||
coFreeTy *types.Signature
|
||||
coAllocTy *types.Signature
|
||||
coNoopTy *types.Signature
|
||||
coFrameTy *types.Signature
|
||||
coIDTy *types.Signature
|
||||
// coIDAsyncTy *types.Signature
|
||||
// coIDRetconTy *types.Signature
|
||||
// coIDRetconOnceTy *types.Signature
|
||||
coEndTy *types.Signature
|
||||
// coEndResultsTy *types.Signature
|
||||
// coEndAsyncTy *types.Signature
|
||||
coSuspendTy *types.Signature
|
||||
// coSaveTy *types.Signature
|
||||
// coSuspendAsyncTy *types.Signature
|
||||
// coPrepareAsyncTy *types.Signature
|
||||
// coSuspendRetconTy *types.Signature
|
||||
// coAwaitSuspendFunctionTy *types.Signature
|
||||
// coAwaitSuspendVoidTy *types.Signature
|
||||
// coAwaitSuspendBoolTy *types.Signature
|
||||
// coAwaitSuspendHandleTy *types.Signature
|
||||
|
||||
paramObjPtr_ *types.Var
|
||||
|
||||
ptrSize int
|
||||
@@ -441,6 +482,18 @@ func (p Program) Any() Type {
|
||||
return p.anyTy
|
||||
}
|
||||
|
||||
func (p Program) Token() Type {
|
||||
if p.tokenTy == nil {
|
||||
p.tokenTy = &aType{p.tyToken(), rawType{types.Typ[types.Invalid]}, vkInvalid}
|
||||
}
|
||||
return p.tokenTy
|
||||
}
|
||||
|
||||
func (p Program) TokenNone() Expr {
|
||||
impl := llvm.ConstNull(p.Token().ll)
|
||||
return Expr{impl: impl, Type: p.Token()}
|
||||
}
|
||||
|
||||
/*
|
||||
// Eface returns the empty interface type.
|
||||
// It is equivalent to Any.
|
||||
@@ -586,6 +639,7 @@ type aPackage struct {
|
||||
named map[types.Type]Expr
|
||||
afterb unsafe.Pointer
|
||||
patch func(types.Type) types.Type
|
||||
fnlink func(string) string
|
||||
|
||||
iRoutine int
|
||||
}
|
||||
@@ -648,9 +702,41 @@ func (p Package) Path() string {
|
||||
return p.abi.Pkg
|
||||
}
|
||||
|
||||
// Find presplitcoroutine attribute and replace attribute tag with it
|
||||
// e.g. attributes #0 = { noinline nounwind readnone "presplitcoroutine" }
|
||||
// replace #0 with presplitcoroutine
|
||||
// and also remove all other attributes
|
||||
func removeLLVMAttributes(ll string) string {
|
||||
attrRe := regexp.MustCompile(`^attributes (#\d+) = {[^}]*}$`)
|
||||
attrRe2 := regexp.MustCompile(`(\) #\d+ {|\) #\d+)$`)
|
||||
lines := strings.Split(ll, "\n")
|
||||
newLines := make([]string, 0, len(lines))
|
||||
presplitcoroutine := ""
|
||||
for _, line := range lines {
|
||||
if m := attrRe.FindStringSubmatch(line); m != nil {
|
||||
if strings.Contains(line, "\"presplitcoroutine\"") {
|
||||
presplitcoroutine = " " + m[1] + " "
|
||||
}
|
||||
} else {
|
||||
newLines = append(newLines, line)
|
||||
}
|
||||
}
|
||||
|
||||
for i, line := range newLines {
|
||||
if presplitcoroutine != "" {
|
||||
line = strings.Replace(line, presplitcoroutine, " presplitcoroutine ", 1)
|
||||
}
|
||||
line = attrRe2.ReplaceAllString(line, ")")
|
||||
newLines[i] = line
|
||||
}
|
||||
|
||||
return strings.Join(newLines, "\n")
|
||||
}
|
||||
|
||||
// String returns a string representation of the package.
|
||||
func (p Package) String() string {
|
||||
return p.mod.String()
|
||||
// TODO(lijie): workaround for compiling errors of LLVM attributes
|
||||
return removeLLVMAttributes(p.mod.String())
|
||||
}
|
||||
|
||||
// SetPatch sets a patch function.
|
||||
@@ -658,6 +744,11 @@ func (p Package) SetPatch(fn func(types.Type) types.Type) {
|
||||
p.patch = fn
|
||||
}
|
||||
|
||||
// SetResolveLinkname sets a function to resolve linkname.
|
||||
func (p Package) SetResolveLinkname(fn func(string) string) {
|
||||
p.fnlink = fn
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
func (p Package) afterBuilder() Builder {
|
||||
|
||||
232
ssa/ssa_test.go
232
ssa/ssa_test.go
@@ -469,6 +469,47 @@ _llgo_0:
|
||||
`)
|
||||
}
|
||||
|
||||
func TestSwitch(t *testing.T) {
|
||||
prog := NewProgram(nil)
|
||||
pkg := prog.NewPackage("bar", "foo/bar")
|
||||
params := types.NewTuple(types.NewVar(0, nil, "a", types.Typ[types.Int]))
|
||||
rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int]))
|
||||
sig := types.NewSignatureType(nil, nil, nil, params, rets, false)
|
||||
fn := pkg.NewFunc("fn", sig, InGo)
|
||||
b := fn.MakeBody(4)
|
||||
cond := fn.Param(0)
|
||||
case1 := fn.Block(1)
|
||||
case2 := fn.Block(2)
|
||||
defb := fn.Block(3)
|
||||
swc := b.Switch(cond, defb)
|
||||
swc.Case(prog.Val(1), case1)
|
||||
swc.Case(prog.Val(2), case2)
|
||||
swc.End(b)
|
||||
b.SetBlock(case1).Return(prog.Val(3))
|
||||
b.SetBlock(case2).Return(prog.Val(4))
|
||||
b.SetBlock(defb).Return(prog.Val(5))
|
||||
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
||||
source_filename = "foo/bar"
|
||||
|
||||
define i64 @fn(i64 %0) {
|
||||
_llgo_0:
|
||||
switch i64 %0, label %_llgo_3 [
|
||||
i64 1, label %_llgo_1
|
||||
i64 2, label %_llgo_2
|
||||
]
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
ret i64 3
|
||||
|
||||
_llgo_2: ; preds = %_llgo_0
|
||||
ret i64 4
|
||||
|
||||
_llgo_3: ; preds = %_llgo_0
|
||||
ret i64 5
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
||||
func TestUnOp(t *testing.T) {
|
||||
prog := NewProgram(nil)
|
||||
pkg := prog.NewPackage("bar", "foo/bar")
|
||||
@@ -552,3 +593,194 @@ _llgo_0:
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
||||
func TestPointerOffset(t *testing.T) {
|
||||
prog := NewProgram(nil)
|
||||
pkg := prog.NewPackage("bar", "foo/bar")
|
||||
params := types.NewTuple(
|
||||
types.NewVar(0, nil, "p", types.NewPointer(types.Typ[types.Int])),
|
||||
types.NewVar(0, nil, "offset", types.Typ[types.Int]),
|
||||
)
|
||||
rets := types.NewTuple(types.NewVar(0, nil, "", types.NewPointer(types.Typ[types.Int])))
|
||||
sig := types.NewSignatureType(nil, nil, nil, params, rets, false)
|
||||
fn := pkg.NewFunc("fn", sig, InGo)
|
||||
b := fn.MakeBody(1)
|
||||
ptr := fn.Param(0)
|
||||
offset := fn.Param(1)
|
||||
result := b.OffsetPtr(ptr, offset)
|
||||
b.Return(result)
|
||||
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
||||
source_filename = "foo/bar"
|
||||
|
||||
define ptr @fn(ptr %0, i64 %1) {
|
||||
_llgo_0:
|
||||
%2 = getelementptr ptr, ptr %0, i64 %1
|
||||
ret ptr %2
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
||||
func TestLLVMTrap(t *testing.T) {
|
||||
prog := NewProgram(nil)
|
||||
pkg := prog.NewPackage("bar", "foo/bar")
|
||||
b := pkg.NewFunc("fn", NoArgsNoRet, InGo).MakeBody(1)
|
||||
b.LLVMTrap()
|
||||
b.Unreachable()
|
||||
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
||||
source_filename = "foo/bar"
|
||||
|
||||
define void @fn() {
|
||||
_llgo_0:
|
||||
call void @llvm.trap()
|
||||
unreachable
|
||||
}
|
||||
|
||||
; Function Attrs: cold noreturn nounwind memory(inaccessiblemem: write)
|
||||
declare void @llvm.trap()
|
||||
|
||||
`)
|
||||
}
|
||||
|
||||
func TestCoroFuncs(t *testing.T) {
|
||||
prog := NewProgram(nil)
|
||||
pkg := prog.NewPackage("bar", "foo/bar")
|
||||
fn := pkg.NewFunc("fn", NoArgsNoRet, InGo)
|
||||
entryBlk := fn.MakeBlock("entry")
|
||||
suspdBlk := fn.MakeBlock("suspend")
|
||||
cleanBlk := fn.MakeBlock("clean")
|
||||
b := fn.NewBuilder()
|
||||
b.async = true
|
||||
|
||||
b.SetBlock(entryBlk)
|
||||
align := b.Const(constant.MakeInt64(0), prog.Int32())
|
||||
align8 := b.Const(constant.MakeInt64(8), prog.Int32())
|
||||
null := b.Const(nil, b.Prog.CIntPtr())
|
||||
b.promise = null
|
||||
id := b.CoID(align, null, null, null)
|
||||
bf := b.Const(constant.MakeBool(false), prog.Bool())
|
||||
b.CoSizeI32()
|
||||
size := b.CoSizeI64()
|
||||
b.CoAlignI32()
|
||||
b.CoAlignI64()
|
||||
b.CoAlloc(id)
|
||||
frame := b.Alloca(size)
|
||||
hdl := b.CoBegin(id, frame)
|
||||
|
||||
b.SetBlock(cleanBlk)
|
||||
b.CoFree(id, frame)
|
||||
b.Jump(suspdBlk)
|
||||
|
||||
b.SetBlock(suspdBlk)
|
||||
b.CoEnd(hdl, bf, prog.TokenNone())
|
||||
// b.Return(b.promise)
|
||||
|
||||
b.SetBlock(entryBlk)
|
||||
|
||||
b.CoResume(hdl)
|
||||
b.CoDone(hdl)
|
||||
b.CoDestroy(hdl)
|
||||
|
||||
b.CoPromise(null, align8, bf)
|
||||
b.CoNoop()
|
||||
b.CoFrame()
|
||||
setArgs := types.NewTuple(types.NewVar(0, nil, "value", types.Typ[types.Int]))
|
||||
setSig := types.NewSignatureType(nil, nil, nil, setArgs, nil, false)
|
||||
setFn := pkg.NewFunc("setValue", setSig, InGo)
|
||||
one := b.Const(constant.MakeInt64(1), prog.Int())
|
||||
|
||||
b.onSuspBlk = func(next BasicBlock) (BasicBlock, BasicBlock, BasicBlock) {
|
||||
return suspdBlk, next, cleanBlk
|
||||
}
|
||||
b.CoYield(setFn, one, bf)
|
||||
b.CoReturn(setFn, one)
|
||||
|
||||
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
||||
source_filename = "foo/bar"
|
||||
|
||||
define void @fn() {
|
||||
entry:
|
||||
%0 = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
|
||||
%1 = call i32 @llvm.coro.size.i32()
|
||||
%2 = call i64 @llvm.coro.size.i64()
|
||||
%3 = call i32 @llvm.coro.align.i32()
|
||||
%4 = call i64 @llvm.coro.align.i64()
|
||||
%5 = call i1 @llvm.coro.alloc(token %0)
|
||||
%6 = alloca i8, i64 %2, align 1
|
||||
%7 = call ptr @llvm.coro.begin(token %0, ptr %6)
|
||||
call void @llvm.coro.resume(ptr %7)
|
||||
%8 = call i1 @llvm.coro.done(ptr %7)
|
||||
%9 = zext i1 %8 to i64
|
||||
%10 = trunc i64 %9 to i8
|
||||
call void @llvm.coro.destroy(ptr %7)
|
||||
%11 = call ptr @llvm.coro.promise(ptr null, i32 8, i1 false)
|
||||
%12 = call ptr @llvm.coro.noop()
|
||||
%13 = call ptr @llvm.coro.frame()
|
||||
call void @setValue(ptr null, i64 1)
|
||||
%14 = call i8 @llvm.coro.suspend(<null operand!>, i1 false)
|
||||
switch i8 %14, label %suspend [
|
||||
i8 0, label %suspend
|
||||
i8 1, label %clean
|
||||
]
|
||||
|
||||
suspend: ; preds = %entry, %entry, %clean
|
||||
%15 = call i1 @llvm.coro.end(ptr %7, i1 false, token none)
|
||||
call void @setValue(ptr null, i64 1)
|
||||
br label %clean
|
||||
|
||||
clean: ; preds = %suspend, %entry
|
||||
%16 = call ptr @llvm.coro.free(token %0, ptr %6)
|
||||
br label %suspend
|
||||
|
||||
_llgo_3: ; No predecessors!
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: read)
|
||||
declare token @llvm.coro.id(i32, ptr readnone, ptr nocapture readonly, ptr)
|
||||
|
||||
; Function Attrs: nounwind memory(none)
|
||||
declare i32 @llvm.coro.size.i32()
|
||||
|
||||
; Function Attrs: nounwind memory(none)
|
||||
declare i64 @llvm.coro.size.i64()
|
||||
|
||||
; Function Attrs: nounwind memory(none)
|
||||
declare i32 @llvm.coro.align.i32()
|
||||
|
||||
; Function Attrs: nounwind memory(none)
|
||||
declare i64 @llvm.coro.align.i64()
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i1 @llvm.coro.alloc(token)
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @llvm.coro.begin(token, ptr writeonly)
|
||||
|
||||
; Function Attrs: nounwind memory(argmem: read)
|
||||
declare ptr @llvm.coro.free(token, ptr nocapture readonly)
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i1 @llvm.coro.end(ptr, i1, token)
|
||||
|
||||
declare void @llvm.coro.resume(ptr)
|
||||
|
||||
; Function Attrs: nounwind memory(argmem: readwrite)
|
||||
declare i1 @llvm.coro.done(ptr nocapture readonly)
|
||||
|
||||
declare void @llvm.coro.destroy(ptr)
|
||||
|
||||
; Function Attrs: nounwind memory(none)
|
||||
declare ptr @llvm.coro.promise(ptr nocapture, i32, i1)
|
||||
|
||||
; Function Attrs: nounwind memory(none)
|
||||
declare ptr @llvm.coro.noop()
|
||||
|
||||
; Function Attrs: nounwind memory(none)
|
||||
declare ptr @llvm.coro.frame()
|
||||
|
||||
declare void @setValue(i64)
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i8 @llvm.coro.suspend(token, i1)
|
||||
|
||||
`)
|
||||
}
|
||||
|
||||
@@ -63,6 +63,13 @@ type aBuilder struct {
|
||||
Func Function
|
||||
Pkg Package
|
||||
Prog Program
|
||||
|
||||
async bool
|
||||
asyncToken Expr
|
||||
promise Expr
|
||||
onSuspBlk func(blk BasicBlock) (susp BasicBlock, next BasicBlock, clean BasicBlock)
|
||||
onReturn func()
|
||||
blkOffset int
|
||||
}
|
||||
|
||||
// Builder represents a builder for creating instructions in a function.
|
||||
@@ -94,7 +101,7 @@ func (b Builder) setBlockMoveLast(blk BasicBlock) (next BasicBlock) {
|
||||
|
||||
impl := b.impl
|
||||
|
||||
next = b.Func.MakeBlock()
|
||||
next = b.Func.MakeBlock("")
|
||||
impl.SetInsertPointAtEnd(next.last)
|
||||
impl.Insert(last)
|
||||
|
||||
@@ -288,7 +295,7 @@ func (b Builder) Times(n Expr, loop func(i Expr)) {
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/*
|
||||
|
||||
type caseStmt struct {
|
||||
v llvm.Value
|
||||
blk llvm.BasicBlock
|
||||
@@ -326,7 +333,7 @@ func (b Builder) Switch(v Expr, defb BasicBlock) Switch {
|
||||
}
|
||||
return &aSwitch{v.impl, defb.first, nil}
|
||||
}
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Phi represents a phi node.
|
||||
|
||||
14
ssa/type.go
14
ssa/type.go
@@ -58,6 +58,7 @@ const (
|
||||
vkIface
|
||||
vkStruct
|
||||
vkChan
|
||||
vkToken
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -282,6 +283,13 @@ func (p Program) tyInt() llvm.Type {
|
||||
return p.intType
|
||||
}
|
||||
|
||||
func (p Program) tyToken() llvm.Type {
|
||||
if p.tokenType.IsNil() {
|
||||
p.tokenType = p.ctx.TokenType()
|
||||
}
|
||||
return p.tokenType
|
||||
}
|
||||
|
||||
func llvmIntType(ctx llvm.Context, size int) llvm.Type {
|
||||
if size <= 4 {
|
||||
return ctx.Int32Type()
|
||||
@@ -362,6 +370,9 @@ func (p Program) toType(raw types.Type) Type {
|
||||
return &aType{p.rtString(), typ, vkString}
|
||||
case types.UnsafePointer:
|
||||
return &aType{p.tyVoidPtr(), typ, vkPtr}
|
||||
// TODO(lijie): temporary solution, should be replaced by a proper type
|
||||
case types.Invalid:
|
||||
return &aType{p.tyToken(), typ, vkInvalid}
|
||||
}
|
||||
case *types.Pointer:
|
||||
elem := p.rawType(t.Elem())
|
||||
@@ -444,7 +455,8 @@ func (p Program) toLLVMTypes(t *types.Tuple, n int) (ret []llvm.Type) {
|
||||
if n > 0 {
|
||||
ret = make([]llvm.Type, n)
|
||||
for i := 0; i < n; i++ {
|
||||
ret[i] = p.rawType(t.At(i).Type()).ll
|
||||
pt := t.At(i)
|
||||
ret[i] = p.rawType(pt.Type()).ll
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
229
x/async/_demo/asyncdemo/asyncdemo.go
Normal file
229
x/async/_demo/asyncdemo/asyncdemo.go
Normal file
@@ -0,0 +1,229 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/goplus/llgo/x/async"
|
||||
"github.com/goplus/llgo/x/tuple"
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
func http(method string, url string, callback func(resp *Response, err error)) {
|
||||
go func() {
|
||||
body := ""
|
||||
if strings.HasPrefix(url, "http://example.com/user/") {
|
||||
name := url[len("http://example.com/user/"):]
|
||||
body = `{"name":"` + name + `"}`
|
||||
} else if strings.HasPrefix(url, "http://example.com/score/") {
|
||||
body = "99.5"
|
||||
}
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
resp := &Response{StatusCode: 200, Body: body}
|
||||
callback(resp, nil)
|
||||
}()
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type Response struct {
|
||||
StatusCode int
|
||||
|
||||
Body string
|
||||
}
|
||||
|
||||
func (r *Response) Text() (co async.Promise[tuple.Tuple2[string, error]]) {
|
||||
co.Return(tuple.Tuple2[string, error]{V1: r.Body, V2: nil})
|
||||
return
|
||||
}
|
||||
|
||||
// async AsyncHttpGet(url string) (resp *Response, err error) {
|
||||
// http("GET", url, func(resp *Response, err error) {
|
||||
// return resp, err
|
||||
// })
|
||||
// }
|
||||
func AsyncHttpGet(url string) (co async.Promise[tuple.Tuple2[*Response, error]]) {
|
||||
return co.Async(func(resolve func(tuple.Tuple2[*Response, error])) {
|
||||
http("GET", url, func(resp *Response, err error) {
|
||||
resolve(tuple.Tuple2[*Response, error]{V1: resp, V2: nil})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func AsyncHttpPost(url string) (co async.Promise[tuple.Tuple2[*Response, error]]) {
|
||||
http("POST", url, func(resp *Response, err error) {
|
||||
// co.Return(tuple.Tuple2[*Response, error]{V1: resp, V2: nil})
|
||||
})
|
||||
co.Suspend()
|
||||
return
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type User struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func GetUser(name string) (co async.Promise[tuple.Tuple2[User, error]]) {
|
||||
resp, err := AsyncHttpGet("http://example.com/user/" + name).Await().Values()
|
||||
if err != nil {
|
||||
// return User{}, err
|
||||
co.Return(tuple.Tuple2[User, error]{V1: User{}, V2: err})
|
||||
return
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
// return User{}, fmt.Errorf("http status code: %d", resp.StatusCode)
|
||||
co.Return(tuple.Tuple2[User, error]{V1: User{}, V2: fmt.Errorf("http status code: %d", resp.StatusCode)})
|
||||
return
|
||||
}
|
||||
|
||||
body, err := resp.Text().Await().Values()
|
||||
if err != nil {
|
||||
// return User{}, err
|
||||
co.Return(tuple.Tuple2[User, error]{V1: User{}, V2: err})
|
||||
return
|
||||
}
|
||||
user := User{}
|
||||
if err := json.Unmarshal([]byte(body), &user); err != nil {
|
||||
// return User{}, err
|
||||
co.Return(tuple.Tuple2[User, error]{V1: User{}, V2: err})
|
||||
return
|
||||
}
|
||||
|
||||
// return user, nil
|
||||
co.Return(tuple.Tuple2[User, error]{V1: user, V2: nil})
|
||||
return
|
||||
}
|
||||
|
||||
func GetScore() (co *async.Promise[tuple.Tuple2[float64, error]]) {
|
||||
resp, err := AsyncHttpGet("http://example.com/score/").Await().Values()
|
||||
if err != nil {
|
||||
co.Return(tuple.Tuple2[float64, error]{V1: 0, V2: err})
|
||||
return
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
// return 0, fmt.Errorf("http status code: %d", resp.StatusCode)
|
||||
co.Return(tuple.Tuple2[float64, error]{V1: 0, V2: fmt.Errorf("http status code: %d", resp.StatusCode)})
|
||||
return
|
||||
}
|
||||
|
||||
body, err := resp.Text().Await().Values()
|
||||
if err != nil {
|
||||
// return 0, err
|
||||
co.Return(tuple.Tuple2[float64, error]{V1: 0, V2: err})
|
||||
return
|
||||
}
|
||||
|
||||
score := 0.0
|
||||
if _, err := fmt.Sscanf(body, "%f", &score); err != nil {
|
||||
// return 0, err
|
||||
co.Return(tuple.Tuple2[float64, error]{V1: 0, V2: err})
|
||||
return
|
||||
}
|
||||
|
||||
// return score, nil
|
||||
co.Return(tuple.Tuple2[float64, error]{V1: score, V2: nil})
|
||||
return
|
||||
}
|
||||
|
||||
func DoUpdate(op string) (co *async.Promise[error]) {
|
||||
resp, err := AsyncHttpPost("http://example.com/update/" + op).Await().Values()
|
||||
if err != nil {
|
||||
co.Return(err)
|
||||
return
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
co.Return(fmt.Errorf("http status code: %d", resp.StatusCode))
|
||||
}
|
||||
|
||||
co.Return(nil)
|
||||
return
|
||||
}
|
||||
|
||||
func GenInts() (co *async.Promise[int]) {
|
||||
co.Yield(3)
|
||||
co.Yield(2)
|
||||
co.Yield(5)
|
||||
return
|
||||
}
|
||||
|
||||
// Generator with async calls and panic
|
||||
func GenUsers() (co *async.Promise[User]) {
|
||||
u, err := GetUser("Alice").Await().Values()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
co.Yield(u)
|
||||
u, err = GetUser("Bob").Await().Values()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
co.Yield(u)
|
||||
u, err = GetUser("Cindy").Await().Values()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
co.Yield(u)
|
||||
log.Printf("genUsers done\n")
|
||||
return
|
||||
}
|
||||
|
||||
func Demo() (co *async.Promise[async.Void]) {
|
||||
user, err := GetUser("1").Await().Values()
|
||||
log.Println(user, err)
|
||||
|
||||
// user, err = naive.Race[tuple.Tuple2[User, error]](GetUser("2"), GetUser("3"), GetUser("4")).Value().Values()
|
||||
// log.Println(user, err)
|
||||
|
||||
// users := naive.All[tuple.Tuple2[User, error]]([]naive.AsyncCall[tuple.Tuple2[User, error]]{GetUser("5"), GetUser("6"), GetUser("7")}).Value()
|
||||
// log.Println(users, err)
|
||||
|
||||
// user, score, _ := naive.Await3Compiled[User, float64, async.Void](GetUser("8"), GetScore(), DoUpdate("update sth.")).Value().Values()
|
||||
// log.Println(user, score, err)
|
||||
|
||||
// for loop with generator
|
||||
g := GenInts()
|
||||
for !g.Done() {
|
||||
log.Println("genInt:", g.Value(), g.Done())
|
||||
g.Resume()
|
||||
}
|
||||
|
||||
// for loop with async generator
|
||||
// for u, err := range GenUsers() {...}
|
||||
g1 := GenUsers()
|
||||
for !g1.Done() {
|
||||
u := g1.Value()
|
||||
log.Println("genUser:", u)
|
||||
g1.Resume()
|
||||
}
|
||||
|
||||
// TODO(lijie): select from multiple promises without channel
|
||||
// select {
|
||||
// case user := <-GetUser("123").Chan():
|
||||
// log.Println("user:", user)
|
||||
// case score := <-GetScore().Chan():
|
||||
// log.Println("score:", score)
|
||||
// case <-async.Timeout(5 * time.Second).Chan():
|
||||
// log.Println("timeout")
|
||||
// }
|
||||
|
||||
log.Println("Demo done")
|
||||
co.Return(async.Void{})
|
||||
return
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.SetFlags(log.Lshortfile | log.LstdFlags)
|
||||
|
||||
log.Printf("=========== Run Demo ===========\n")
|
||||
v1 := Demo()
|
||||
log.Println(v1)
|
||||
log.Printf("=========== Run Demo finished ===========\n")
|
||||
}
|
||||
26
x/async/_demo/gendemo/gendemo.go
Normal file
26
x/async/_demo/gendemo/gendemo.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/goplus/llgo/x/async"
|
||||
)
|
||||
|
||||
func GenInts() (co *async.Promise[int]) {
|
||||
println("gen: 1")
|
||||
co.Yield(1)
|
||||
println("gen: 2")
|
||||
co.Yield(2)
|
||||
println("gen: 3")
|
||||
co.Yield(3)
|
||||
return
|
||||
}
|
||||
|
||||
func main() {
|
||||
co := GenInts()
|
||||
for !co.Done() {
|
||||
fmt.Printf("got: %v\n", co.Value())
|
||||
co.Next()
|
||||
}
|
||||
fmt.Printf("done\n")
|
||||
}
|
||||
77
x/async/async.go
Normal file
77
x/async/async.go
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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 async
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
_ "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
LLGoPackage = "decl"
|
||||
)
|
||||
|
||||
type Void = [0]byte
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type Promise[TOut any] struct {
|
||||
hdl unsafe.Pointer
|
||||
value TOut
|
||||
}
|
||||
|
||||
// // llgo:link (*Promise).Await llgo.coAwait
|
||||
func (p *Promise[TOut]) Await() TOut {
|
||||
panic("should not executed")
|
||||
}
|
||||
|
||||
func (p *Promise[TOut]) Return(v TOut) {
|
||||
p.value = v
|
||||
coReturn(p.hdl)
|
||||
}
|
||||
|
||||
// llgo:link (*Promise).Yield llgo.coYield
|
||||
func (p *Promise[TOut]) Yield(v TOut) {}
|
||||
|
||||
// llgo:link (*Promise).Suspend llgo.coSuspend
|
||||
func (p *Promise[TOut]) Suspend() {}
|
||||
|
||||
func (p *Promise[TOut]) Resume() {
|
||||
coResume(p.hdl)
|
||||
}
|
||||
|
||||
func (p *Promise[TOut]) Next() {
|
||||
coResume(p.hdl)
|
||||
}
|
||||
|
||||
// TODO(lijie): should merge to Yield()
|
||||
// call by llgo.coYield
|
||||
func (p *Promise[TOut]) setValue(v TOut) {
|
||||
p.value = v
|
||||
}
|
||||
|
||||
func (p *Promise[TOut]) Value() TOut {
|
||||
return p.value
|
||||
}
|
||||
|
||||
func (p *Promise[TOut]) Done() bool {
|
||||
return coDone(p.hdl) != 0
|
||||
}
|
||||
|
||||
func Run[TOut any](f func() TOut) TOut {
|
||||
panic("should not executed")
|
||||
}
|
||||
39
x/async/coro.go
Normal file
39
x/async/coro.go
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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 async
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
_ "unsafe"
|
||||
|
||||
"github.com/goplus/llgo/c"
|
||||
)
|
||||
|
||||
// llgo:link coDone llgo.coDone
|
||||
func coDone(hdl unsafe.Pointer) c.Char {
|
||||
panic("should not executed")
|
||||
}
|
||||
|
||||
// llgo:link coResume llgo.coResume
|
||||
func coResume(hdl unsafe.Pointer) {
|
||||
panic("should not executed")
|
||||
}
|
||||
|
||||
// llgo:link coReturn llgo.coReturn
|
||||
func coReturn(hdl unsafe.Pointer) {
|
||||
panic("should not executed")
|
||||
}
|
||||
@@ -1,290 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/goplus/llgo/x/io"
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type Response struct {
|
||||
StatusCode int
|
||||
|
||||
mockBody string
|
||||
}
|
||||
|
||||
func (r *Response) mock(body string) {
|
||||
r.mockBody = body
|
||||
}
|
||||
|
||||
func (r *Response) Text() (resolve io.Promise[string]) {
|
||||
resolve(r.mockBody, nil)
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Response) TextCompiled() *io.PromiseImpl[string] {
|
||||
P := &io.PromiseImpl[string]{}
|
||||
P.Func = func(resolve func(string, error)) {
|
||||
for {
|
||||
switch P.Prev = P.Next; P.Prev {
|
||||
case 0:
|
||||
resolve(r.mockBody, nil)
|
||||
P.Next = -1
|
||||
return
|
||||
default:
|
||||
panic("Promise already done")
|
||||
}
|
||||
}
|
||||
}
|
||||
return P
|
||||
}
|
||||
|
||||
func HttpGet(url string, callback func(resp *Response, err error)) {
|
||||
resp := &Response{StatusCode: 200}
|
||||
callback(resp, nil)
|
||||
}
|
||||
|
||||
func AsyncHttpGet(url string) (resolve io.Promise[*Response]) {
|
||||
HttpGet(url, resolve)
|
||||
return
|
||||
}
|
||||
|
||||
func AsyncHttpGetCompiled(url string) *io.PromiseImpl[*Response] {
|
||||
P := &io.PromiseImpl[*Response]{}
|
||||
P.Func = func(resolve func(*Response, error)) {
|
||||
for {
|
||||
switch P.Prev = P.Next; P.Prev {
|
||||
case 0:
|
||||
HttpGet(url, resolve)
|
||||
P.Next = -1
|
||||
return
|
||||
default:
|
||||
panic("Promise already done")
|
||||
}
|
||||
}
|
||||
}
|
||||
return P
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type User struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func GetUser(uid string) (resolve io.Promise[User]) {
|
||||
resp, err := AsyncHttpGet("http://example.com/user/" + uid).Await()
|
||||
if err != nil {
|
||||
resolve(User{}, err)
|
||||
return
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
resolve(User{}, fmt.Errorf("http status code: %d", resp.StatusCode))
|
||||
return
|
||||
}
|
||||
|
||||
resp.mock(`{"name":"Alice"}`)
|
||||
|
||||
body, err := resp.Text().Await()
|
||||
if err != nil {
|
||||
resolve(User{}, err)
|
||||
return
|
||||
}
|
||||
user := User{}
|
||||
if err := json.Unmarshal([]byte(body), &user); err != nil {
|
||||
resolve(User{}, err)
|
||||
return
|
||||
}
|
||||
|
||||
resolve(user, nil)
|
||||
return
|
||||
}
|
||||
|
||||
func GetUserCompiled(uid string) *io.PromiseImpl[User] {
|
||||
var state1 *io.PromiseImpl[*Response]
|
||||
var state2 *io.PromiseImpl[string]
|
||||
|
||||
P := &io.PromiseImpl[User]{}
|
||||
P.Func = func(resolve func(User, error)) {
|
||||
for {
|
||||
switch P.Prev = P.Next; P.Prev {
|
||||
case 0:
|
||||
state1 = AsyncHttpGetCompiled("http://example.com/user/" + uid)
|
||||
P.Next = 1
|
||||
return
|
||||
case 1:
|
||||
state1.EnsureDone()
|
||||
resp, err := state1.Value, state1.Err
|
||||
if err != nil {
|
||||
resolve(User{}, err)
|
||||
return
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
resolve(User{}, fmt.Errorf("http status code: %d", resp.StatusCode))
|
||||
return
|
||||
}
|
||||
|
||||
resp.mock(`{"name":"Alice"}`)
|
||||
|
||||
state2 = resp.TextCompiled()
|
||||
P.Next = 2
|
||||
return
|
||||
case 2:
|
||||
state2.EnsureDone()
|
||||
body, err := state2.Value, state2.Err
|
||||
if err != nil {
|
||||
resolve(User{}, err)
|
||||
return
|
||||
}
|
||||
user := User{}
|
||||
if err := json.Unmarshal([]byte(body), &user); err != nil {
|
||||
resolve(User{}, err)
|
||||
return
|
||||
}
|
||||
|
||||
resolve(user, nil)
|
||||
P.Next = -1
|
||||
return
|
||||
default:
|
||||
panic("Promise already done")
|
||||
}
|
||||
}
|
||||
}
|
||||
return P
|
||||
}
|
||||
|
||||
func GetScore() *io.Promise[float64] {
|
||||
panic("todo: GetScore")
|
||||
}
|
||||
|
||||
func GetScoreCompiled() *io.PromiseImpl[float64] {
|
||||
P := &io.PromiseImpl[float64]{}
|
||||
P.Func = func(resolve func(float64, error)) {
|
||||
for {
|
||||
switch P.Prev = P.Next; P.Prev {
|
||||
case 0:
|
||||
panic("todo: GetScore")
|
||||
default:
|
||||
panic("Promise already done")
|
||||
}
|
||||
}
|
||||
}
|
||||
return P
|
||||
}
|
||||
|
||||
func DoUpdate(op string) *io.Promise[io.Void] {
|
||||
panic("todo: DoUpdate")
|
||||
}
|
||||
|
||||
func DoUpdateCompiled(op string) *io.PromiseImpl[io.Void] {
|
||||
P := &io.PromiseImpl[io.Void]{}
|
||||
P.Func = func(resolve func(io.Void, error)) {
|
||||
for {
|
||||
switch P.Prev = P.Next; P.Prev {
|
||||
case 0:
|
||||
panic("todo: DoUpdate")
|
||||
default:
|
||||
panic("Promise already done")
|
||||
}
|
||||
}
|
||||
}
|
||||
return P
|
||||
}
|
||||
|
||||
func Demo() (resolve io.Promise[io.Void]) {
|
||||
user, err := GetUser("123").Await()
|
||||
log.Println(user, err)
|
||||
|
||||
user, err = io.Race[User](GetUser("123"), GetUser("456"), GetUser("789")).Await()
|
||||
log.Println(user, err)
|
||||
|
||||
users, err := io.All[User]([]io.AsyncCall[User]{GetUser("123"), GetUser("456"), GetUser("789")}).Await()
|
||||
log.Println(users, err)
|
||||
|
||||
user, score, _, err := io.Await3[User, float64, io.Void](GetUser("123"), GetScore(), DoUpdate("update sth."))
|
||||
log.Println(user, score, err)
|
||||
|
||||
// TODO(lijie): select from multiple promises without channel
|
||||
select {
|
||||
case user := <-GetUser("123").Chan():
|
||||
log.Println("user:", user)
|
||||
case score := <-GetScore().Chan():
|
||||
log.Println("score:", score)
|
||||
case <-io.Timeout(5 * time.Second).Chan():
|
||||
log.Println("timeout")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func DemoCompiled() *io.PromiseImpl[io.Void] {
|
||||
var state1 *io.PromiseImpl[User]
|
||||
var state2 *io.PromiseImpl[User]
|
||||
var state3 *io.PromiseImpl[[]User]
|
||||
var state4 *io.PromiseImpl[io.Await3Result[User, float64, io.Void]]
|
||||
|
||||
P := &io.PromiseImpl[io.Void]{}
|
||||
P.Func = func(resolve func(io.Void, error)) {
|
||||
for {
|
||||
switch P.Prev = P.Next; P.Prev {
|
||||
case 0:
|
||||
state1 = GetUserCompiled("123")
|
||||
P.Next = 1
|
||||
return
|
||||
case 1:
|
||||
state1.EnsureDone()
|
||||
user, err := state1.Value, state1.Err
|
||||
log.Printf("user: %v, err: %v\n", user, err)
|
||||
|
||||
state2 = io.Race[User](GetUserCompiled("123"), GetUserCompiled("456"), GetUserCompiled("789"))
|
||||
P.Next = 2
|
||||
return
|
||||
case 2:
|
||||
state2.EnsureDone()
|
||||
user, err := state2.Value, state2.Err
|
||||
log.Println(user, err)
|
||||
|
||||
state3 = io.All[User]([]io.AsyncCall[User]{GetUserCompiled("123"), GetUserCompiled("456"), GetUserCompiled("789")})
|
||||
P.Next = 3
|
||||
return
|
||||
case 3:
|
||||
state3.EnsureDone()
|
||||
users, err := state3.Value, state3.Err
|
||||
log.Println(users, err)
|
||||
|
||||
state4 = io.Await3Compiled[User, float64, io.Void](GetUserCompiled("123"), GetScoreCompiled(), DoUpdateCompiled("update sth."))
|
||||
P.Next = 4
|
||||
return
|
||||
case 4:
|
||||
state4.EnsureDone()
|
||||
user, score, _, err := state4.Value.V1, state4.Value.V2, state4.Value.V3, state4.Value.Err
|
||||
log.Println(user, score, err)
|
||||
|
||||
select {
|
||||
case user := <-GetUserCompiled("123").Chan():
|
||||
log.Println("user:", user)
|
||||
case score := <-GetScoreCompiled().Chan():
|
||||
log.Println("score:", score)
|
||||
case <-io.TimeoutCompiled(5 * time.Second).Chan():
|
||||
log.Println("timeout")
|
||||
}
|
||||
P.Next = -1
|
||||
return
|
||||
default:
|
||||
panic("Promise already done")
|
||||
}
|
||||
}
|
||||
}
|
||||
return P
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.SetFlags(log.Lshortfile | log.LstdFlags)
|
||||
// io.Run(Demo())
|
||||
io.Run(DemoCompiled())
|
||||
}
|
||||
170
x/io/io.go
170
x/io/io.go
@@ -1,170 +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 io
|
||||
|
||||
import (
|
||||
_ "unsafe"
|
||||
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
LLGoPackage = "decl"
|
||||
)
|
||||
|
||||
type Void = [0]byte
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type AsyncCall[OutT any] interface {
|
||||
Await(timeout ...time.Duration) (ret OutT, err error)
|
||||
Chan() <-chan OutT
|
||||
EnsureDone()
|
||||
}
|
||||
|
||||
// llgo:link AsyncCall.Await llgo.await
|
||||
func Await[OutT any](call AsyncCall[OutT], timeout ...time.Duration) (ret OutT, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
//go:linkname Timeout llgo.timeout
|
||||
func Timeout(time.Duration) (ret AsyncCall[Void])
|
||||
|
||||
func TimeoutCompiled(d time.Duration) *PromiseImpl[Void] {
|
||||
P := &PromiseImpl[Void]{}
|
||||
P.Func = func(resolve func(Void, error)) {
|
||||
go func() {
|
||||
time.Sleep(d)
|
||||
resolve(Void{}, nil)
|
||||
}()
|
||||
}
|
||||
return P
|
||||
}
|
||||
|
||||
// llgo:link Race llgo.race
|
||||
func Race[OutT any](acs ...AsyncCall[OutT]) (ret *PromiseImpl[OutT]) {
|
||||
return
|
||||
}
|
||||
|
||||
func All[OutT any](acs []AsyncCall[OutT]) (ret *PromiseImpl[[]OutT]) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// llgo:link Await2 llgo.await
|
||||
func Await2[OutT1, OutT2 any](
|
||||
ac1 AsyncCall[OutT1], ac2 AsyncCall[OutT2],
|
||||
timeout ...time.Duration) (ret1 OutT1, ret2 OutT2, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
type Await2Result[T1 any, T2 any] struct {
|
||||
V1 T1
|
||||
V2 T2
|
||||
Err error
|
||||
}
|
||||
|
||||
func Await2Compiled[OutT1, OutT2 any](
|
||||
ac1 AsyncCall[OutT1], ac2 AsyncCall[OutT2],
|
||||
timeout ...time.Duration) (ret *PromiseImpl[Await2Result[OutT1, OutT2]]) {
|
||||
return
|
||||
}
|
||||
|
||||
// llgo:link Await3 llgo.await
|
||||
func Await3[OutT1, OutT2, OutT3 any](
|
||||
ac1 AsyncCall[OutT1], ac2 AsyncCall[OutT2], ac3 AsyncCall[OutT3],
|
||||
timeout ...time.Duration) (ret1 OutT1, ret2 OutT2, ret3 OutT3, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
type Await3Result[T1 any, T2 any, T3 any] struct {
|
||||
V1 T1
|
||||
V2 T2
|
||||
V3 T3
|
||||
Err error
|
||||
}
|
||||
|
||||
func Await3Compiled[OutT1, OutT2, OutT3 any](
|
||||
ac1 AsyncCall[OutT1], ac2 AsyncCall[OutT2], ac3 AsyncCall[OutT3],
|
||||
timeout ...time.Duration) (ret *PromiseImpl[Await3Result[OutT1, OutT2, OutT3]]) {
|
||||
return
|
||||
}
|
||||
|
||||
func Run(ac AsyncCall[Void]) {
|
||||
p := ac.(*PromiseImpl[Void])
|
||||
p.Resume()
|
||||
<-ac.Chan()
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type Promise[OutT any] func(OutT, error)
|
||||
|
||||
// llgo:link Promise.Await llgo.await
|
||||
func (p Promise[OutT]) Await(timeout ...time.Duration) (ret OutT, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (p Promise[OutT]) Chan() <-chan OutT {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p Promise[OutT]) EnsureDone() {
|
||||
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type PromiseImpl[TOut any] struct {
|
||||
Func func(resolve func(TOut, error))
|
||||
Value TOut
|
||||
Err error
|
||||
Prev int
|
||||
Next int
|
||||
|
||||
c chan TOut
|
||||
}
|
||||
|
||||
func (p *PromiseImpl[TOut]) Resume() {
|
||||
p.Func(func(v TOut, err error) {
|
||||
p.Value = v
|
||||
p.Err = err
|
||||
})
|
||||
}
|
||||
|
||||
func (p *PromiseImpl[TOut]) EnsureDone() {
|
||||
if p.Next == -1 {
|
||||
panic("Promise already done")
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PromiseImpl[TOut]) Chan() <-chan TOut {
|
||||
if p.c == nil {
|
||||
p.c = make(chan TOut, 1)
|
||||
p.Func(func(v TOut, err error) {
|
||||
p.Value = v
|
||||
p.Err = err
|
||||
p.c <- v
|
||||
})
|
||||
}
|
||||
return p.c
|
||||
}
|
||||
|
||||
func (p *PromiseImpl[TOut]) Await(timeout ...time.Duration) (ret TOut, err error) {
|
||||
panic("should not called")
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
57
x/tuple/tuple.go
Normal file
57
x/tuple/tuple.go
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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 tuple
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type Tuple1[T any] struct {
|
||||
V1 T
|
||||
}
|
||||
|
||||
func (r Tuple1[T]) Values() T {
|
||||
return r.V1
|
||||
}
|
||||
|
||||
type Tuple2[T1 any, T2 any] struct {
|
||||
V1 T1
|
||||
V2 T2
|
||||
}
|
||||
|
||||
func (r Tuple2[T1, T2]) Values() (T1, T2) {
|
||||
return r.V1, r.V2
|
||||
}
|
||||
|
||||
type Tuple3[T1 any, T2 any, T3 any] struct {
|
||||
V1 T1
|
||||
V2 T2
|
||||
V3 T3
|
||||
}
|
||||
|
||||
func (r Tuple3[T1, T2, T3]) Values() (T1, T2, T3) {
|
||||
return r.V1, r.V2, r.V3
|
||||
}
|
||||
|
||||
type Tuple4[T1 any, T2 any, T3 any, T4 any] struct {
|
||||
V1 T1
|
||||
V2 T2
|
||||
V3 T3
|
||||
V4 T4
|
||||
}
|
||||
|
||||
func (r Tuple4[T1, T2, T3, T4]) Values() (T1, T2, T3, T4) {
|
||||
return r.V1, r.V2, r.V3, r.V4
|
||||
}
|
||||
Reference in New Issue
Block a user